diff --git a/source/includes/vst3sdk/CMakeLists.txt b/source/includes/vst3sdk/CMakeLists.txt new file mode 100644 index 000000000..0ede9b426 --- /dev/null +++ b/source/includes/vst3sdk/CMakeLists.txt @@ -0,0 +1,220 @@ + +cmake_minimum_required (VERSION 3.4.3) + +#------------------------------------------------------------------------------- +# Includes +#------------------------------------------------------------------------------- + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") + +include(Global) +include(AddVST3Library) +include(Bundle) +include(ExportedSymbols) +include(PrefixHeader) +include(PlatformIOS) + +# do not build VST2 by default +option(SMTG_CREATE_VST2_VERSION "Use VST2" OFF) + +#------------------------------------------------------------------------------- +# SDK Project +#------------------------------------------------------------------------------- +set(VST_SDK TRUE) +project(vstsdk) + +if (LINUX) + option(SMTG_ADD_ADDRESS_SANITIZER_CONFIG "Add AddressSanitizer Config (Linux only)" OFF) + if(SMTG_ADD_ADDRESS_SANITIZER_CONFIG) + set(CMAKE_CONFIGURATION_TYPES "${CMAKE_CONFIGURATION_TYPES};ASan") + add_compile_options($<$:-DDEVELOPMENT=1>) + add_compile_options($<$:-fsanitize=address>) + add_compile_options($<$:-DVSTGUI_LIVE_EDITING=1>) + add_compile_options($<$:-g>) + add_compile_options($<$:-O0>) + set(ASAN_LIBRARY asan) + link_libraries($<$:${ASAN_LIBRARY}>) + endif() +endif() + +if(UNIX) + if(XCODE) + set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++14") + set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") + elseif(APPLE) + set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -stdlib=libc++") + link_libraries(c++) + else() + set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wno-multichar") + link_libraries(stdc++fs pthread dl) + endif() + +elseif(WIN) + add_definitions(-D_UNICODE) + add_compile_options(/fp:fast) + add_compile_options($<$:/Oi>) # Enable Intrinsic Functions (Yes) + add_compile_options($<$:/Ot>) # Favor Size Or Speed (Favor fast code) + #add_compile_options($<$:/Ox>) # Optimization (/O2: Maximise Speed /0x: Full Optimization) + add_compile_options($<$:/GF>) # Enable String Pooling + add_compile_options($<$:/EHa>) # Enable C++ Exceptions + add_compile_options($<$:/Oy>) # Omit Frame Pointers +endif() + +set(ROOT "${CMAKE_CURRENT_SOURCE_DIR}") + +# here you can define where the VST3 SDK is located +set(SDK_ROOT "${ROOT}") + +# here you can define where the VSTGUI is located +set(VSTGUI_ROOT "${ROOT}") + +include_directories(${ROOT} ${SDK_ROOT}) + +set(SDK_IDE_LIBS_FOLDER FOLDER "Libraries") +set(SDK_IDE_PLUGIN_EXAMPLES_FOLDER FOLDER "PlugInExamples") +set(SDK_IDE_HOSTING_EXAMPLES_FOLDER FOLDER "HostingExamples") + +#------------------------------------------------------------------------------- +if(MAC AND XCODE) + if(NOT SMTG_COREAUDIO_SDK_PATH) + # Check if the CoreAudio SDK is next to the VST3SDK: + if(EXISTS "${SDK_ROOT}/../CoreAudio/AudioUnits/AUPublic/AUBase/AUBase.cpp") + set(SMTG_COREAUDIO_SDK_PATH "${SDK_ROOT}/../CoreAudio") + else() + if(EXISTS "${SDK_ROOT}/external.apple.coreaudio/AudioUnits/AUPublic/AUBase/AUBase.cpp") + set(SMTG_COREAUDIO_SDK_PATH "${SDK_ROOT}/external.apple.coreaudio") + endif() + endif() + else() + if(NOT IS_ABSOLUTE ${SMTG_COREAUDIO_SDK_PATH}) + get_filename_component(SMTG_COREAUDIO_SDK_PATH "${SDK_ROOT}/${SMTG_COREAUDIO_SDK_PATH}" ABSOLUTE) + endif() + if(NOT EXISTS "${SMTG_COREAUDIO_SDK_PATH}/AudioUnits/AUPublic/AUBase/AUBase.cpp") + message(FATAL_ERROR "SMTG_COREAUDIO_SDK_PATH is set but does not point to an expected location") + endif() + endif() + if(SMTG_COREAUDIO_SDK_PATH) + message(STATUS "SMTG_COREAUDIO_SDK_PATH is set to : " ${SMTG_COREAUDIO_SDK_PATH}) + endif() +endif() + +#------------------------------------------------------------------------------- +# Projects +#------------------------------------------------------------------------------- + +add_subdirectory(base) +add_subdirectory(public.sdk) +add_subdirectory(public.sdk/source/vst/auwrapper) +add_subdirectory(public.sdk/source/vst/auwrapper/again) +add_subdirectory(public.sdk/source/vst/interappaudio) +add_subdirectory(public.sdk/samples/vst/again) +add_subdirectory(public.sdk/samples/vst/adelay) +add_subdirectory(public.sdk/samples/vst/channelcontext) +add_subdirectory(public.sdk/samples/vst/hostchecker) +add_subdirectory(public.sdk/samples/vst/editorhost) +add_subdirectory(public.sdk/samples/vst/mda-vst3) +add_subdirectory(public.sdk/samples/vst/note_expression_synth) +add_subdirectory(public.sdk/samples/vst/note_expression_text) +add_subdirectory(public.sdk/samples/vst/pitchnames) +add_subdirectory(public.sdk/samples/vst/prefetchablesupport) +add_subdirectory(public.sdk/samples/vst/programchange) +add_subdirectory(public.sdk/samples/vst/validator) +add_subdirectory(public.sdk/samples/vst/InterAppAudio) + +set(VSTGUI_DISABLE_UNITTESTS 1) +add_subdirectory(vstgui4/vstgui) + +#------------------------------------------------------------------------------- +# VSTGUI Support Library +#------------------------------------------------------------------------------- +add_compile_options($<$:-DVSTGUI_LIVE_EDITING=1>) +set(VST3_VSTGUI_SOURCES + ${VSTGUI_ROOT}/vstgui4/vstgui/plugin-bindings/vst3groupcontroller.cpp + ${VSTGUI_ROOT}/vstgui4/vstgui/plugin-bindings/vst3groupcontroller.h + ${VSTGUI_ROOT}/vstgui4/vstgui/plugin-bindings/vst3padcontroller.cpp + ${VSTGUI_ROOT}/vstgui4/vstgui/plugin-bindings/vst3padcontroller.h + ${VSTGUI_ROOT}/vstgui4/vstgui/plugin-bindings/vst3editor.cpp + ${VSTGUI_ROOT}/vstgui4/vstgui/plugin-bindings/vst3editor.h + ${SDK_ROOT}/public.sdk/source/vst/vstguieditor.cpp +) +add_library(vstgui_support STATIC ${VST3_VSTGUI_SOURCES}) +target_include_directories(vstgui_support PUBLIC ${VSTGUI_ROOT}/vstgui4) +target_link_libraries(vstgui_support PRIVATE vstgui_uidescription) +if(MAC) + if(XCODE) + target_link_libraries(vstgui_support PRIVATE "-framework Cocoa" "-framework OpenGL" "-framework Accelerate" "-framework QuartzCore" "-framework Carbon") + else() + find_library(COREFOUNDATION_FRAMEWORK CoreFoundation) + find_library(COCOA_FRAMEWORK Cocoa) + find_library(OPENGL_FRAMEWORK OpenGL) + find_library(ACCELERATE_FRAMEWORK Accelerate) + find_library(QUARTZCORE_FRAMEWORK QuartzCore) + find_library(CARBON_FRAMEWORK Carbon) + target_link_libraries(vstgui_support PRIVATE ${COREFOUNDATION_FRAMEWORK} ${COCOA_FRAMEWORK} ${OPENGL_FRAMEWORK} ${ACCELERATE_FRAMEWORK} ${QUARTZCORE_FRAMEWORK} ${CARBON_FRAMEWORK}) + endif() +endif() + +#------------------------------------------------------------------------------- +# IDE sorting +#------------------------------------------------------------------------------- +set_target_properties(vstgui_support PROPERTIES ${SDK_IDE_LIBS_FOLDER}) +set_target_properties(sdk PROPERTIES ${SDK_IDE_LIBS_FOLDER}) +set_target_properties(base PROPERTIES ${SDK_IDE_LIBS_FOLDER}) +set_target_properties(vstgui PROPERTIES ${SDK_IDE_LIBS_FOLDER}) +set_target_properties(vstgui_uidescription PROPERTIES ${SDK_IDE_LIBS_FOLDER}) + +if (TARGET again) + set_target_properties(again PROPERTIES ${SDK_IDE_PLUGIN_EXAMPLES_FOLDER}) +endif() +if (TARGET againsimple) + set_target_properties(againsimple PROPERTIES ${SDK_IDE_PLUGIN_EXAMPLES_FOLDER}) +endif() +if (TARGET adelay) + set_target_properties(adelay PROPERTIES ${SDK_IDE_PLUGIN_EXAMPLES_FOLDER}) +endif() +if (TARGET channelcontext) + set_target_properties(channelcontext PROPERTIES ${SDK_IDE_PLUGIN_EXAMPLES_FOLDER}) +endif() +if (TARGET hostchecker) + set_target_properties(hostchecker PROPERTIES ${SDK_IDE_PLUGIN_EXAMPLES_FOLDER}) +endif() +if (TARGET mda-vst3) + set_target_properties(mda-vst3 PROPERTIES ${SDK_IDE_PLUGIN_EXAMPLES_FOLDER}) +endif() +if (TARGET noteexpressionsynth) + set_target_properties(noteexpressionsynth PROPERTIES ${SDK_IDE_PLUGIN_EXAMPLES_FOLDER}) +endif() +if (TARGET noteexpressiontext) + set_target_properties(noteexpressiontext PROPERTIES ${SDK_IDE_PLUGIN_EXAMPLES_FOLDER}) +endif() +if (TARGET pitchnames) + set_target_properties(pitchnames PROPERTIES ${SDK_IDE_PLUGIN_EXAMPLES_FOLDER}) +endif() +if (TARGET prefetchablesupport) + set_target_properties(prefetchablesupport PROPERTIES ${SDK_IDE_PLUGIN_EXAMPLES_FOLDER}) +endif() +if (TARGET programchange) + set_target_properties(programchange PROPERTIES ${SDK_IDE_PLUGIN_EXAMPLES_FOLDER}) +endif() + +if (TARGET editorhost) + set_target_properties(editorhost PROPERTIES ${SDK_IDE_HOSTING_EXAMPLES_FOLDER}) +endif() +if (TARGET validator) + set_target_properties(validator PROPERTIES ${SDK_IDE_HOSTING_EXAMPLES_FOLDER}) +endif () + +if(MAC AND XCODE) + if(SMTG_COREAUDIO_SDK_PATH) + set_target_properties(auwrapper PROPERTIES ${SDK_IDE_LIBS_FOLDER}) + set_target_properties(again_au PROPERTIES ${SDK_IDE_PLUGIN_EXAMPLES_FOLDER}) + endif() + if(IOS_DEVELOPMENT_TEAM) + set_target_properties(sdk_ios PROPERTIES ${SDK_IDE_LIBS_FOLDER}) + set_target_properties(base_ios PROPERTIES ${SDK_IDE_LIBS_FOLDER}) + set_target_properties(interappaudio PROPERTIES ${SDK_IDE_LIBS_FOLDER}) + set_target_properties(noteexpressionsynth_ios PROPERTIES ${SDK_IDE_PLUGIN_EXAMPLES_FOLDER}) + endif() +endif() diff --git a/source/includes/vst3sdk/LICENSE.txt b/source/includes/vst3sdk/LICENSE.txt new file mode 100644 index 000000000..e8af8518e --- /dev/null +++ b/source/includes/vst3sdk/LICENSE.txt @@ -0,0 +1,41 @@ +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +This license applies only to files referencing this license, +for other files of the Software Development Kit the respective embedded license text +is applicable. The license can be found at: www.steinberg.net/sdklicenses_vst3 + +This Software Development Kit is licensed under the terms of the Steinberg VST3 License, +or alternatively under the terms of the General Public License (GPL) Version 3. +You may use the Software Development Kit according to either of these licenses as it is +most appropriate for your project on a case-by-case basis (commercial or not). + +a) Proprietary Steinberg VST3 License +The Software Development Kit may not be distributed in parts or its entirety +without prior written agreement by Steinberg Media Technologies GmbH. +The SDK must not be used to re-engineer or manipulate any technology used +in any Steinberg or Third-party application or software module, +unless permitted by law. +Neither the name of the Steinberg Media Technologies GmbH nor the names of its +contributors may be used to endorse or promote products derived from this +software without specific prior written permission. +Before publishing a software under the proprietary license, you need to obtain a copy +of the License Agreement signed by Steinberg Media Technologies GmbH. +The Steinberg VST SDK License Agreement can be found at: +www.steinberg.net/en/company/developers.html + +THE SDK IS PROVIDED BY STEINBERG MEDIA TECHNOLOGIES GMBH "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL STEINBERG MEDIA TECHNOLOGIES GMBH BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +OF THE POSSIBILITY OF SUCH DAMAGE. + +b) General Public License (GPL) Version 3 +Details of these licenses can be found at: www.gnu.org/licenses/gpl-3.0.html +//---------------------------------------------------------------------------------- diff --git a/source/includes/vst3sdk/README.md b/source/includes/vst3sdk/README.md new file mode 100644 index 000000000..960968039 --- /dev/null +++ b/source/includes/vst3sdk/README.md @@ -0,0 +1,92 @@ +# Welcome to VST SDK 3.6.7 +## The VST SDK package contains: +- The VST 3 API +- VST 3 Implementation Helper Classes +- AU and VST2 wrappers +- VST 3 Plug-ins Examples + +The full VST 3 SDK is available [here!](https://www.steinberg.net/en/company/developers.html). It contains a VST 3 Plug-in Test Host Application/Validator. +## System requirements + +Supported OS: + +- Microsoft Windows 7-10 +- Apple OSX 10.7-10.12 +- Apple iOS 8-9 +- Linux (Preview) + +Supported IDE: +- Visual Studio 2013/2015 +- Xcode 6/7 +- Qt Creator + +--- + +## About VST Plug-ins in general +A VST Plug-in is an audio processing component that is utilized within a host application. This host application provides the audio or/and event streams that are processed by the Plug-in's code. Generally speaking, a VST Plug-in can take a stream of audio data, apply a process to the audio, and return the result to the host application. A VST Plug-in performs its process normally using the processor of the computer. The audio stream is broken down into a series of blocks. The host supplies the blocks in sequence. The host and its current environment control the block-size. The VST Plug-in maintains the status of all its own parameters relating to the running process: The host does not maintain any information about what the Plug-in did with the last block of data it processed. + +From the host application's point of view, a VST Plug-in is a black box with an arbitrary number of inputs, outputs (Event (MIDI) or Audio), and associated parameters. The host needs no implicit knowledge of the Plug-in's process to be able to use it. The Plug-in process can use whatever parameters it wishes, internally to the process, but depending on the capabilities of the host, it can allow the changes to user parameters to be automated by the host. + +The source code of a VST Plug-in is platform independent, but the delivery system depends on the platform architecture: +- On **Windows**, a VST Plug-in is a multi-threaded DLL (Dynamic Link Library). +- On **Mac OS X**, a VST Plug-in is a Mach-O Bundle +- On **Linux**, a VST Plug-in is a package + +To learn more about VST you can subscribe to the [VST Developer Forum](https://sdk.steinberg.net) - check the 3rd Party Developer Support section at [www.steinberg.net](www.steinberg.net). + + --- + +## About VST 3 +VST 3 is a general rework of the long-serving VST Plug-in interface. It is not compatible with the older VST versions, but it includes some new features and possibilities. We have redesigned the API to make it not only far easier and more reliable for developers to work with, but have also provided completely new possibilities for Plug-ins. These include: + +### 1. Improved Performance with the Silence Flag +Processing can optionally be applied to Plug-ins only when audio signals are present on their respective inputs, so VST 3 Plug-ins can apply their processing economically and only when it is needed. + +### 2. Multiple Dynamic I/Os +VST 3 Plug-ins are no longer limited to a fixed number of inputs and outputs, and their I/O configuration can dynamically adapt to the channel configuration. Side-chains are also very easily realizable. This includes the possibility to deactivate unused buses after loading and even reactivate those when needed. This cleans up the mixer and further helps to reduce CPU load. + +### 3. Sample-accurate Automation +VST 3 also features vastly improved parameter automation with sample accuracy and support for ramped automation data, allowing completely accurate and rapid parameter automation changes. + +### 4. Logical Parameter Organization +The VST 3 Plug-in parameters are displayed in a tree structure. Parameters are grouped into sections which represent the structure of the Plug-in. Plug-ins can communicate their internal structure for the purpose of overview, but also for some associated functionality (eg. program-lists). + +### 5. Resizeable UI Editor +VST 3 defines a way to allow resizing of the Plug-in editor by a user. + +### 6. Mouse Over Support +The Host could ask the Plug-in which parameter is under the mouse. + +### 7. Context Menu Support +VST 3 defines a way to allow the host to add its own entries in the Plug-in context menu of a specific parameter. + +### 8. Channel Context Information +A VST 3 Plug-in could access some channel information where it is instantiated: name, color,... + +### 9. Note Expression +VST 3 defines with Note Expression a new way of event controller editing. The Plug-in is able to break free from the limitations of MIDI controller events by providing access to new VST 3 controller events that circumvent the laws of MIDI and provide articulation information for each individual note (event) in a polyphonic arrangement according to its noteId. + +### 10. 3D Support +VST 3 supports new speaker configurations like Atmos, Auro 3D or 22.2. + +### 11. Factory Concept +VST 3 Plug-in library could export multiple Plug-ins and in this way replaces the shell concept of VST 2 (kPlugCategShell). + +### 12. Support Remote control Representation +VST 3 Plug-in can deliver a specific parameter mapping for remote controls like Nuage. + +### 13. Others +While designing VST 3, we performed a careful analysis of the existing functionality of VST and rewrote the interfaces from scratch. In doing so, we focused a lot on providing clear interfaces and their documentation in order to avoid usage errors from the deepest possible layer. +Some more features implemented specifically for developers include: +- More stable technical Host/Plug-in environment +- Advanced technical definition of the standard +- Modular approach +- Separation of UI and processing +- Advanced Preset System +- Multiple Plug-ins per Library +- Test Host included +- Automated Testing Environment +- Validator (small command line Test Host) and Plug-in examples code included. +--- +## License +More details are found at [www.steinberg.net/sdklicenses_vst3](www.steinberg.net/sdklicenses_vst3) \ No newline at end of file diff --git a/source/includes/vst3sdk/public.sdk/CMakeLists.txt b/source/includes/vst3sdk/public.sdk/CMakeLists.txt new file mode 100644 index 000000000..76826b23a --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/CMakeLists.txt @@ -0,0 +1,85 @@ + +set(sdk_sources + source/common/pluginview.cpp + source/main/pluginfactoryvst3.cpp + source/vst/vstaudioeffect.cpp + source/vst/vstbus.cpp + source/vst/vstbypassprocessor.cpp + source/vst/vstcomponent.cpp + source/vst/vstcomponentbase.cpp + source/vst/vsteditcontroller.cpp + source/vst/vstinitiids.cpp + source/vst/vstnoteexpressiontypes.cpp + source/vst/vstparameters.cpp + source/vst/vstpresetfile.cpp + source/vst/vstrepresentation.cpp +) + +set(pluginterfaces_sources + ${SDK_ROOT}/pluginterfaces/base/falignpop.h + ${SDK_ROOT}/pluginterfaces/base/falignpush.h + ${SDK_ROOT}/pluginterfaces/base/fplatform.h + ${SDK_ROOT}/pluginterfaces/base/fstrdefs.h + ${SDK_ROOT}/pluginterfaces/base/ftypes.h + ${SDK_ROOT}/pluginterfaces/base/funknown.h + ${SDK_ROOT}/pluginterfaces/base/futils.h + ${SDK_ROOT}/pluginterfaces/base/fvariant.h + ${SDK_ROOT}/pluginterfaces/base/geoconstants.h + ${SDK_ROOT}/pluginterfaces/base/ibstream.h + ${SDK_ROOT}/pluginterfaces/base/icloneable.h + ${SDK_ROOT}/pluginterfaces/base/ierrorcontext.h + ${SDK_ROOT}/pluginterfaces/base/ipersistent.h + ${SDK_ROOT}/pluginterfaces/base/ipluginbase.h + ${SDK_ROOT}/pluginterfaces/base/istringresult.h + ${SDK_ROOT}/pluginterfaces/base/iupdatehandler.h + ${SDK_ROOT}/pluginterfaces/base/keycodes.h + ${SDK_ROOT}/pluginterfaces/base/pluginbasefwd.h + ${SDK_ROOT}/pluginterfaces/base/smartpointer.h + ${SDK_ROOT}/pluginterfaces/base/ucolorspec.h + ${SDK_ROOT}/pluginterfaces/base/conststringtable.cpp + ${SDK_ROOT}/pluginterfaces/base/conststringtable.h + ${SDK_ROOT}/pluginterfaces/base/funknown.cpp + ${SDK_ROOT}/pluginterfaces/base/ustring.cpp + ${SDK_ROOT}/pluginterfaces/base/ustring.h +) + +set(vst_includes + ${SDK_ROOT}/pluginterfaces/gui/iplugview.h + ${SDK_ROOT}/pluginterfaces/gui/iplugviewcontentscalesupport.h + ${SDK_ROOT}/pluginterfaces/vst/ivstattributes.h + ${SDK_ROOT}/pluginterfaces/vst/ivstaudioprocessor.h + ${SDK_ROOT}/pluginterfaces/vst/ivstautomationstate.h + ${SDK_ROOT}/pluginterfaces/vst/ivstchannelcontextinfo.h + ${SDK_ROOT}/pluginterfaces/vst/ivstcomponent.h + ${SDK_ROOT}/pluginterfaces/vst/ivstcontextmenu.h + ${SDK_ROOT}/pluginterfaces/vst/ivsteditcontroller.h + ${SDK_ROOT}/pluginterfaces/vst/ivstevents.h + ${SDK_ROOT}/pluginterfaces/vst/ivsthostapplication.h + ${SDK_ROOT}/pluginterfaces/vst/ivstinterappaudio.h + ${SDK_ROOT}/pluginterfaces/vst/ivstmessage.h + ${SDK_ROOT}/pluginterfaces/vst/ivstmidicontrollers.h + ${SDK_ROOT}/pluginterfaces/vst/ivstnoteexpression.h + ${SDK_ROOT}/pluginterfaces/vst/ivstparameterchanges.h + ${SDK_ROOT}/pluginterfaces/vst/ivstplugview.h + ${SDK_ROOT}/pluginterfaces/vst/ivstprefetchablesupport.h + ${SDK_ROOT}/pluginterfaces/vst/ivstprocesscontext.h + ${SDK_ROOT}/pluginterfaces/vst/ivstrepresentation.h + ${SDK_ROOT}/pluginterfaces/vst/ivstunits.h + ${SDK_ROOT}/pluginterfaces/vst/vstpresetkeys.h + ${SDK_ROOT}/pluginterfaces/vst/vstpshpack4.h + ${SDK_ROOT}/pluginterfaces/vst/vsttypes.h +) + +add_library(sdk STATIC ${sdk_sources} ${pluginterfaces_sources} ${vst_includes}) +target_link_libraries(sdk PRIVATE base) + +# iOS target +if(MAC AND XCODE AND IOS_DEVELOPMENT_TEAM) + add_library(sdk_ios STATIC ${sdk_sources} ${pluginterfaces_sources} ${vst_includes}) + smtg_set_platform_ios(sdk_ios) + target_link_libraries(sdk_ios PRIVATE base_ios) +endif() + +source_group("public.sdk" FILES ${sdk_sources}) +source_group("vst" FILES ${vst_includes}) +source_group("base" FILES ${pluginterfaces_sources}) diff --git a/source/includes/vst3sdk/public.sdk/LICENSE.txt b/source/includes/vst3sdk/public.sdk/LICENSE.txt new file mode 100644 index 000000000..c017a1ed7 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/LICENSE.txt @@ -0,0 +1,27 @@ +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- diff --git a/source/includes/vst3sdk/public.sdk/source/common/memorystream.cpp b/source/includes/vst3sdk/public.sdk/source/common/memorystream.cpp new file mode 100644 index 000000000..831db302d --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/common/memorystream.cpp @@ -0,0 +1,319 @@ +//----------------------------------------------------------------------------- +// Project : SDK Core +// +// Category : Common Classes +// Filename : public.sdk/source/common/memorystream.cpp +// Created by : Steinberg, 03/2008 +// Description : IBStream Implementation for memory blocks +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "memorystream.h" +#include "pluginterfaces/base/futils.h" +#include + +namespace Steinberg { + +//----------------------------------------------------------------------------- +IMPLEMENT_FUNKNOWN_METHODS (MemoryStream, IBStream, IBStream::iid) +static const TSize kMemGrowAmount = 4096; + +//----------------------------------------------------------------------------- +MemoryStream::MemoryStream (void* data, TSize length) +: memory ((char*)data) +, memorySize (length) +, size (length) +, cursor (0) +, ownMemory (false) +, allocationError (false) +{ + FUNKNOWN_CTOR +} + +//----------------------------------------------------------------------------- +MemoryStream::MemoryStream () +: memory (nullptr) +, memorySize (0) +, size (0) +, cursor (0) +, ownMemory (true) +, allocationError (false) +{ + FUNKNOWN_CTOR +} + +//----------------------------------------------------------------------------- +MemoryStream::~MemoryStream () +{ + if (ownMemory && memory) + ::free (memory); + + FUNKNOWN_DTOR +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API MemoryStream::read (void* data, int32 numBytes, int32* numBytesRead) +{ + if (memory == nullptr) + { + if (allocationError) + return kOutOfMemory; + numBytes = 0; + } + else + { + // Does read exceed size ? + if (cursor + numBytes > size) + { + int32 maxBytes = int32 (size - cursor); + + // Has length become zero or negative ? + if (maxBytes <= 0) + { + cursor = size; + numBytes = 0; + } + else + numBytes = maxBytes; + } + + if (numBytes) + { + memcpy (data, &memory[cursor], numBytes); + cursor += numBytes; + } + } + + if (numBytesRead) + *numBytesRead = numBytes; + + return kResultTrue; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API MemoryStream::write (void* buffer, int32 numBytes, int32* numBytesWritten) +{ + if (allocationError) + return kOutOfMemory; + if (buffer == nullptr) + return kInvalidArgument; + + // Does write exceed size ? + TSize requiredSize = cursor + numBytes; + if (requiredSize > size) + { + if (requiredSize > memorySize) + setSize (requiredSize); + else + size = requiredSize; + } + + // Copy data + if (memory && cursor >= 0 && numBytes > 0) + { + memcpy (&memory[cursor], buffer, numBytes); + // Update cursor + cursor += numBytes; + } + else + numBytes = 0; + + if (numBytesWritten) + *numBytesWritten = numBytes; + + return kResultTrue; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API MemoryStream::seek (int64 pos, int32 mode, int64* result) +{ + switch (mode) + { + case kIBSeekSet: + cursor = pos; + break; + case kIBSeekCur: + cursor = cursor + pos; + break; + case kIBSeekEnd: + cursor = size + pos; + break; + } + + if (ownMemory == false) + if (cursor > memorySize) + cursor = memorySize; + + if (result) + *result = cursor; + + return kResultTrue; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API MemoryStream::tell (int64* pos) +{ + if (!pos) + return kInvalidArgument; + + *pos = cursor; + return kResultTrue; +} + +//------------------------------------------------------------------------ +TSize MemoryStream::getSize () +{ + return size; +} + +//------------------------------------------------------------------------ +void MemoryStream::setSize (TSize s) +{ + if (s <= 0) + { + if (ownMemory && memory) + free (memory); + + memory = nullptr; + memorySize = 0; + size = 0; + cursor = 0; + return; + } + + TSize newMemorySize = (((Max (memorySize, s) - 1) / kMemGrowAmount) + 1) * kMemGrowAmount; + if (newMemorySize == memorySize) + { + size = s; + return; + } + + if (memory && ownMemory == false) + { + allocationError = true; + return; + } + + ownMemory = true; + char* newMemory = nullptr; + + if (memory) + { + newMemory = (char*)realloc (memory, (size_t)newMemorySize); + if (newMemory == nullptr && newMemorySize > 0) + { + newMemory = (char*)malloc ((size_t)newMemorySize); + if (newMemory) + { + memcpy (newMemory, memory, (size_t)Min (newMemorySize, memorySize)); + free (memory); + } + } + } + else + newMemory = (char*)malloc ((size_t)newMemorySize); + + if (newMemory == nullptr) + { + if (newMemorySize > 0) + allocationError = true; + + memory = nullptr; + memorySize = 0; + size = 0; + cursor = 0; + } + else + { + memory = newMemory; + memorySize = newMemorySize; + size = s; + } +} + +//------------------------------------------------------------------------ +char* MemoryStream::getData () +{ + return memory; +} + +//------------------------------------------------------------------------ +char* MemoryStream::detachData () +{ + if (ownMemory) + { + char* result = memory; + memory = nullptr; + memorySize = 0; + size = 0; + cursor = 0; + return result; + } + return nullptr; +} + +//------------------------------------------------------------------------ +bool MemoryStream::truncate () +{ + if (ownMemory == false) + return false; + + if (memorySize == size) + return true; + + memorySize = size; + + if (memorySize == 0) + { + if (memory) + { + free (memory); + memory = nullptr; + } + } + else + { + if (memory) + { + char* newMemory = (char*)realloc (memory, (size_t)memorySize); + if (newMemory) + memory = newMemory; + } + } + return true; +} + +//------------------------------------------------------------------------ +bool MemoryStream::truncateToCursor () +{ + size = cursor; + return truncate (); +} + +} // namespace diff --git a/source/includes/vst3sdk/public.sdk/source/common/memorystream.h b/source/includes/vst3sdk/public.sdk/source/common/memorystream.h new file mode 100644 index 000000000..a4c528600 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/common/memorystream.h @@ -0,0 +1,80 @@ +//----------------------------------------------------------------------------- +// Project : SDK Core +// +// Category : Common Classes +// Filename : public.sdk/source/common/memorystream.h +// Created by : Steinberg, 03/2008 +// Description : IBStream Implementation for memory blocks +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "pluginterfaces/base/ibstream.h" + +namespace Steinberg { + +//------------------------------------------------------------------------ +/** Memory based Stream for IBStream implementation (using malloc). +\ingroup sdkBase +*/ +//------------------------------------------------------------------------ +class MemoryStream : public IBStream +{ +public: + //------------------------------------------------------------------------ + MemoryStream (); + MemoryStream (void* memory, TSize memorySize); ///< reuse a given memory without getting ownership + virtual ~MemoryStream (); + + //---IBStream--------------------------------------- + virtual tresult PLUGIN_API read (void* buffer, int32 numBytes, int32* numBytesRead) SMTG_OVERRIDE; + virtual tresult PLUGIN_API write (void* buffer, int32 numBytes, int32* numBytesWritten) SMTG_OVERRIDE; + virtual tresult PLUGIN_API seek (int64 pos, int32 mode, int64* result) SMTG_OVERRIDE; + virtual tresult PLUGIN_API tell (int64* pos) SMTG_OVERRIDE; + + TSize getSize (); ///< returns the current memory size + void setSize (TSize size); ///< set the memory size, a realloc will occur if memory already used + char* getData (); ///< returns the memory pointer + char* detachData (); ///< returns the memory pointer and give up ownership + bool truncate (); ///< realloc to the current use memory size if needed + bool truncateToCursor (); ///< truncate memory at current cursor position + + //------------------------------------------------------------------------ + DECLARE_FUNKNOWN_METHODS +protected: + char* memory; // memory block + TSize memorySize; // size of the memory block + TSize size; // size of the stream + int64 cursor; // stream pointer + bool ownMemory; // stream has allocated memory itself + bool allocationError; // stream invalid +}; + +} // namespace diff --git a/source/includes/vst3sdk/public.sdk/source/common/pluginview.cpp b/source/includes/vst3sdk/public.sdk/source/common/pluginview.cpp new file mode 100644 index 000000000..92b4bfe9d --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/common/pluginview.cpp @@ -0,0 +1,117 @@ +//----------------------------------------------------------------------------- +// Project : SDK Core +// +// Category : Common Base Classes +// Filename : public.sdk/source/common/pluginview.cpp +// Created by : Steinberg, 01/2004 +// Description : Plug-In View Implementation +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "pluginview.h" + +namespace Steinberg { + +//------------------------------------------------------------------------ +// CPluginView implementation +//------------------------------------------------------------------------ +CPluginView::CPluginView (const ViewRect* _rect) +: rect (0, 0, 0, 0) +, systemWindow (0) +, plugFrame (0) +{ + //TODO FUNKNOWN_CTOR + if (_rect) + rect = *_rect; +} + +//------------------------------------------------------------------------ +CPluginView::~CPluginView () +{ + //TODO FUNKNOWN_DTOR +} + +//------------------------------------------------------------------------ +// TODO IMPLEMENT_REFCOUNT (CPluginView) + +//------------------------------------------------------------------------ +/* TODO +tresult PLUGIN_API CPluginView::queryInterface (const char* iid, void** obj) +{ + QUERY_INTERFACE (iid, obj, Steinberg::FUnknown::iid, IPlugView) + QUERY_INTERFACE (iid, obj, Steinberg::IPlugView::iid, IPlugView) + *obj = 0; + return kNoInterface; +}*/ + + +//------------------------------------------------------------------------ +tresult PLUGIN_API CPluginView::isPlatformTypeSupported (FIDString /*type*/) +{ + return kNotImplemented; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API CPluginView::attached (void* parent, FIDString /*type*/) +{ + systemWindow = parent; + + attachedToParent (); + return kResultOk; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API CPluginView::removed () +{ + systemWindow = 0; + + removedFromParent (); + return kResultOk; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API CPluginView::onSize (ViewRect* newSize) +{ + if (newSize) + rect = *newSize; + return kResultTrue; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API CPluginView::getSize (ViewRect* size) +{ + if (size) + { + *size = rect; + return kResultTrue; + } + return kInvalidArgument; +} + +} // end of namespace diff --git a/source/includes/vst3sdk/public.sdk/source/common/pluginview.h b/source/includes/vst3sdk/public.sdk/source/common/pluginview.h new file mode 100644 index 000000000..776ef5815 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/common/pluginview.h @@ -0,0 +1,102 @@ +//----------------------------------------------------------------------------- +// Project : SDK Core +// +// Category : Common Base Classes +// Filename : public.sdk/source/common/pluginview.h +// Created by : Steinberg, 01/2004 +// Description : Plug-In View Implementation +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "pluginterfaces/gui/iplugview.h" +#include "base/source/fobject.h" + +namespace Steinberg { + +//------------------------------------------------------------------------ +/** Plug-In view default implementation. +\ingroup sdkBase +Can be used as base class for an IPlugView implementation. */ +//------------------------------------------------------------------------ +class CPluginView: public FObject, + public IPlugView +{ +public: +//------------------------------------------------------------------------ + CPluginView (const ViewRect* rect = 0); + virtual ~CPluginView (); + + /** Returns its current frame rectangle. */ + const ViewRect& getRect () const { return rect; } + + /** Sets a new frame rectangle. */ + void setRect (const ViewRect& r) { rect = r; } + + /** Checks if this view is attached to its parent view. */ + bool isAttached () const { return systemWindow != 0; } + + /** Calls when this view will be attached to its parent view. */ + virtual void attachedToParent () {} + + /** Calls when this view will be removed from its parent view. */ + virtual void removedFromParent () {} + + //---from IPlugView------- + tresult PLUGIN_API isPlatformTypeSupported (FIDString type) SMTG_OVERRIDE; + tresult PLUGIN_API attached (void* parent, FIDString type) SMTG_OVERRIDE; + tresult PLUGIN_API removed () SMTG_OVERRIDE; + + tresult PLUGIN_API onWheel (float /*distance*/) SMTG_OVERRIDE { return kResultFalse; } + tresult PLUGIN_API onKeyDown (char16 /*key*/, int16 /*keyMsg*/, int16 /*modifiers*/) SMTG_OVERRIDE { return kResultFalse; } + tresult PLUGIN_API onKeyUp (char16 /*key*/, int16 /*keyMsg*/, int16 /*modifiers*/) SMTG_OVERRIDE { return kResultFalse; } + tresult PLUGIN_API getSize (ViewRect* size) SMTG_OVERRIDE; + tresult PLUGIN_API onSize (ViewRect* newSize) SMTG_OVERRIDE; + + tresult PLUGIN_API onFocus (TBool /*state*/) SMTG_OVERRIDE { return kResultFalse; } + tresult PLUGIN_API setFrame (IPlugFrame* frame) SMTG_OVERRIDE { plugFrame = frame; return kResultTrue; } + + tresult PLUGIN_API canResize () SMTG_OVERRIDE { return kResultFalse; } + tresult PLUGIN_API checkSizeConstraint (ViewRect* /*rect*/) SMTG_OVERRIDE { return kResultFalse; } + + //---Interface------ + OBJ_METHODS (CPluginView, FObject) + DEFINE_INTERFACES + DEF_INTERFACE (IPlugView) + END_DEFINE_INTERFACES (FObject) + REFCOUNT_METHODS(FObject) +//------------------------------------------------------------------------ +protected: + ViewRect rect; + void* systemWindow; + IPlugFrame* plugFrame; +}; + +} // namespace diff --git a/source/includes/vst3sdk/public.sdk/source/main/dllmain.cpp b/source/includes/vst3sdk/public.sdk/source/main/dllmain.cpp new file mode 100644 index 000000000..9ce2d6f40 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/main/dllmain.cpp @@ -0,0 +1,105 @@ +//----------------------------------------------------------------------------- +// Project : SDK Core +// Version : 1.0 +// +// Category : Common Base Classes +// Filename : public.sdk/source/main/dllmain.cpp +// Created by : Steinberg, 01/2004 +// Description : Windows DLL Entry +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "pluginterfaces/base/ftypes.h" + +#include + +#if defined (_MSC_VER) && defined (DEVELOPMENT) + #include +#endif + +#ifdef UNICODE +#define tstrrchr wcsrchr +#else +#define tstrrchr strrchr +#endif + +//------------------------------------------------------------------------ +HINSTANCE ghInst = 0; +void* moduleHandle = 0; +Steinberg::tchar gPath[MAX_PATH] = {0}; + +//------------------------------------------------------------------------ +#define DllExport __declspec( dllexport ) + +//------------------------------------------------------------------------ +extern bool InitModule (); ///< must be provided by Plug-in: called when the library is loaded +extern bool DeinitModule (); ///< must be provided by Plug-in: called when the library is unloaded + +//------------------------------------------------------------------------ +#ifdef __cplusplus +extern "C" { +#endif + bool DllExport InitDll () ///< must be called from host right after loading dll + { + return InitModule (); + } + bool DllExport ExitDll () ///< must be called from host right before unloading dll + { + return DeinitModule (); + } +#ifdef __cplusplus +} // extern "C" +#endif + +//------------------------------------------------------------------------ +BOOL WINAPI DllMain (HINSTANCE hInst, DWORD dwReason, LPVOID /*lpvReserved*/) +{ + if (dwReason == DLL_PROCESS_ATTACH) + { + #if defined (_MSC_VER) && defined (DEVELOPMENT) + _CrtSetReportMode ( _CRT_WARN, _CRTDBG_MODE_DEBUG ); + _CrtSetReportMode ( _CRT_ERROR, _CRTDBG_MODE_DEBUG ); + _CrtSetReportMode ( _CRT_ASSERT, _CRTDBG_MODE_DEBUG ); + int flag = _CrtSetDbgFlag (_CRTDBG_REPORT_FLAG); + _CrtSetDbgFlag (flag | _CRTDBG_LEAK_CHECK_DF); + #endif + + moduleHandle = ghInst = hInst; + + // gets the path of the component + if (GetModuleFileName (ghInst, gPath, MAX_PATH) > 0) + { + Steinberg::tchar* bkslash = tstrrchr (gPath, TEXT ('\\')); + if (bkslash) + gPath[bkslash - gPath + 1] = 0; + } + } + + return TRUE; +} diff --git a/source/includes/vst3sdk/public.sdk/source/main/linuxmain.cpp b/source/includes/vst3sdk/public.sdk/source/main/linuxmain.cpp new file mode 100644 index 000000000..785724678 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/main/linuxmain.cpp @@ -0,0 +1,80 @@ +//----------------------------------------------------------------------------- +// Project : SDK Core +// Version : 1.0 +// +// Category : Common Base Classes +// Filename : public.sdk/source/main/linuxmain.cpp +// Created by : Steinberg, 01/2004 +// Description : Linux Component Entry +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#if defined(__GNUC__) && ((__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1))) +#define EXPORT __attribute__ ((visibility ("default"))) +#else +#define EXPORT +#endif + +//------------------------------------------------------------------------ +void* moduleHandle = 0; + +//------------------------------------------------------------------------ +bool InitModule (); ///< must be provided by Plug-in: called when the library is loaded +bool DeinitModule (); ///< must be provided by Plug-in: called when the library is unloaded + +//------------------------------------------------------------------------ +extern "C" +{ + EXPORT bool ModuleEntry (void*); + EXPORT bool ModuleExit (void); +} + +static int counter {0}; + +//------------------------------------------------------------------------ +bool ModuleEntry (void* sharedLibraryHandle) +{ + if (++counter == 1) + { + moduleHandle = sharedLibraryHandle; + return InitModule (); + } + return true; +} + +//------------------------------------------------------------------------ +bool ModuleExit (void) +{ + if (--counter == 0) + { + moduleHandle = nullptr; + return DeinitModule (); + } + return true; +} diff --git a/source/includes/vst3sdk/public.sdk/source/main/macexport.exp b/source/includes/vst3sdk/public.sdk/source/main/macexport.exp new file mode 100644 index 000000000..0060ddb14 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/main/macexport.exp @@ -0,0 +1,3 @@ +_GetPluginFactory +_bundleEntry +_bundleExit diff --git a/source/includes/vst3sdk/public.sdk/source/main/macmain.cpp b/source/includes/vst3sdk/public.sdk/source/main/macmain.cpp new file mode 100644 index 000000000..d3fba5896 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/main/macmain.cpp @@ -0,0 +1,113 @@ +//----------------------------------------------------------------------------- +// Project : SDK Core +// Version : 1.0 +// +// Category : Common Base Classes +// Filename : public.sdk/source/main/macmain.cpp +// Created by : Steinberg, 01/2004 +// Description : Mac OS X Bundle Entry +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#ifndef __CF_USE_FRAMEWORK_INCLUDES__ +#define __CF_USE_FRAMEWORK_INCLUDES__ 1 +#endif + +#include + +#if defined(__GNUC__) && ((__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1))) +#define EXPORT __attribute__ ((visibility ("default"))) +#else +#define EXPORT +#endif + +//------------------------------------------------------------------------ +CFBundleRef ghInst = 0; +unsigned int bundleRefCounter = 0; // counting for bundleEntry/bundleExit pairs +void* moduleHandle = 0; +#define MAX_PATH 2048 +char gPath[MAX_PATH] = {0}; + +//------------------------------------------------------------------------ +bool InitModule (); ///< must be provided by Plug-in: called when the library is loaded +bool DeinitModule (); ///< must be provided by Plug-in: called when the library is unloaded + +//------------------------------------------------------------------------ +extern "C" +{ + EXPORT bool bundleEntry (CFBundleRef); + EXPORT bool bundleExit (void); +} + +#include + +std::vector< CFBundleRef > gBundleRefs; + +//------------------------------------------------------------------------ +bool bundleEntry (CFBundleRef ref) +{ + if (ref) + { + bundleRefCounter++; + CFRetain (ref); + + // hold all bundle refs until plug-in is fully uninitialized + gBundleRefs.push_back (ref); + + if (!moduleHandle) + { + ghInst = ref; + moduleHandle = ref; + + // optain the bundle path + CFURLRef tempURL = CFBundleCopyBundleURL (ref); + CFURLGetFileSystemRepresentation (tempURL, true, (UInt8*)gPath, MAX_PATH); + CFRelease (tempURL); + } + } + return InitModule (); +} + +//------------------------------------------------------------------------ +bool bundleExit (void) +{ + if (DeinitModule ()) + { + if (--bundleRefCounter == 0) + { // release the CFBundleRef's once all bundleExit clients called in + // there is no way to identify the proper CFBundleRef of the bundleExit call + for (size_t i = 0; i < gBundleRefs.size(); i++) + CFRelease (gBundleRefs[i]); + gBundleRefs.clear(); + } + return true; + } + + return false; +} diff --git a/source/includes/vst3sdk/public.sdk/source/main/pluginfactoryvst3.cpp b/source/includes/vst3sdk/public.sdk/source/main/pluginfactoryvst3.cpp new file mode 100644 index 000000000..cfdb7e311 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/main/pluginfactoryvst3.cpp @@ -0,0 +1,278 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Common Base Classes +// Filename : public.sdk/source/main/pluginfactoryvst3.cpp +// Created by : Steinberg, 01/2004 +// Description : Standard Plug-in Factory +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "pluginfactoryvst3.h" +#include "pluginterfaces/gui/iplugview.h" +#include "pluginterfaces/base/ibstream.h" + +#include + +namespace Steinberg { + +CPluginFactory* gPluginFactory = 0; + +DEF_CLASS_IID (IPluginBase) +DEF_CLASS_IID (IPlugView) +DEF_CLASS_IID (IPlugViewIdleHandler) +DEF_CLASS_IID (IPlugFrame) +DEF_CLASS_IID (IPlugFrameIdle) +DEF_CLASS_IID (IBStream) +DEF_CLASS_IID (IPluginFactory) +DEF_CLASS_IID (IPluginFactory2) +DEF_CLASS_IID (IPluginFactory3) + +//------------------------------------------------------------------------ +// CPluginFactory implementation +//------------------------------------------------------------------------ +CPluginFactory::CPluginFactory (const PFactoryInfo& info) +: classes (0) +, classCount (0) +, maxClassCount (0) +{ + FUNKNOWN_CTOR + + factoryInfo = info; +} + +//------------------------------------------------------------------------ +CPluginFactory::~CPluginFactory () +{ + if (gPluginFactory == this) + gPluginFactory = 0; + + if (classes) + free (classes); + + FUNKNOWN_DTOR +} + +//------------------------------------------------------------------------ +IMPLEMENT_REFCOUNT (CPluginFactory) + +//------------------------------------------------------------------------ +tresult PLUGIN_API CPluginFactory::queryInterface (FIDString _iid, void** obj) +{ + QUERY_INTERFACE (_iid, obj, IPluginFactory::iid, IPluginFactory) + QUERY_INTERFACE (_iid, obj, IPluginFactory2::iid, IPluginFactory2) + QUERY_INTERFACE (_iid, obj, IPluginFactory3::iid, IPluginFactory3) + QUERY_INTERFACE (_iid, obj, FUnknown::iid, IPluginFactory) + *obj = 0; + return kNoInterface; +} + +//------------------------------------------------------------------------ +bool CPluginFactory::registerClass (const PClassInfo* info, + FUnknown* (*createFunc)(void*), void* context) +{ + if (!info || !createFunc) + return false; + + PClassInfo2 info2; + memcpy (&info2, info, sizeof (PClassInfo)); + return registerClass (&info2, createFunc, context); +} + +//------------------------------------------------------------------------ +bool CPluginFactory::registerClass (const PClassInfo2* info, + FUnknown* (*createFunc)(void*), void* context) +{ + if (!info || !createFunc) + return false; + + if (classCount >= maxClassCount) + { + if (!growClasses ()) + return false; + } + + PClassEntry& entry = classes[classCount]; + entry.info8 = *info; + entry.info16.fromAscii (*info); + entry.createFunc = createFunc; + entry.context = context; + entry.isUnicode = false; + + classCount++; + return true; +} + +//------------------------------------------------------------------------ +bool CPluginFactory::registerClass (const PClassInfoW* info, + FUnknown* (*createFunc)(void*), void* context) +{ + if (!info || !createFunc) + return false; + + if (classCount >= maxClassCount) + { + if (!growClasses ()) + return false; + } + + PClassEntry& entry = classes[classCount]; + entry.info16 = *info; + entry.createFunc = createFunc; + entry.context = context; + entry.isUnicode = true; + + classCount++; + return true; +} + +//------------------------------------------------------------------------ +bool CPluginFactory::growClasses () +{ + static const int32 delta = 10; + + size_t size = (maxClassCount + delta) * sizeof (PClassEntry); + void* memory = classes; + + if (!memory) + memory = malloc (size); + else + memory = realloc (memory, size); + + if (!memory) + return false; + + classes = (PClassEntry*)memory; + maxClassCount += delta; + return true; +} + +//------------------------------------------------------------------------ +bool CPluginFactory::isClassRegistered (const FUID& cid) +{ + for (int32 i = 0; i < classCount; i++) + { + if (cid == classes[i].info16.cid) + return true; + } + return false; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API CPluginFactory::getFactoryInfo (PFactoryInfo* info) +{ + if (info) + memcpy (info, &factoryInfo, sizeof (PFactoryInfo)); + return kResultOk; +} + +//------------------------------------------------------------------------ +int32 PLUGIN_API CPluginFactory::countClasses () +{ + return classCount; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API CPluginFactory::getClassInfo (int32 index, PClassInfo* info) +{ + if (info && (index >= 0 && index < classCount)) + { + if (classes[index].isUnicode) + { + memset (info, 0, sizeof (PClassInfo)); + return kResultFalse; + } + + memcpy (info, &classes[index].info8, sizeof (PClassInfo)); + return kResultOk; + } + return kInvalidArgument; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API CPluginFactory::getClassInfo2 (int32 index, PClassInfo2* info) +{ + if (info && (index >= 0 && index < classCount)) + { + if (classes[index].isUnicode) + { + memset (info, 0, sizeof (PClassInfo2)); + return kResultFalse; + } + + memcpy (info, &classes[index].info8, sizeof (PClassInfo2)); + return kResultOk; + } + return kInvalidArgument; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API CPluginFactory::getClassInfoUnicode (int32 index, PClassInfoW* info) +{ + if (info && (index >= 0 && index < classCount)) + { + memcpy (info, &classes[index].info16, sizeof (PClassInfoW)); + return kResultOk; + } + return kInvalidArgument; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API CPluginFactory::createInstance (FIDString cid, FIDString _iid, void** obj) +{ + for (int32 i = 0; i < classCount; i++) + { + if (memcmp (classes[i].info16.cid, cid, sizeof (TUID)) == 0) + { + FUnknown* instance = classes[i].createFunc (classes[i].context); + if (instance) + { + if (instance->queryInterface (_iid, obj) == kResultOk) + { + instance->release (); + return kResultOk; + } + else + instance->release (); + } + break; + } + } + + *obj = 0; + return kNoInterface; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API CPluginFactory::setHostContext (FUnknown* /*context*/) +{ + return kNotImplemented; +} + +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/main/pluginfactoryvst3.h b/source/includes/vst3sdk/public.sdk/source/main/pluginfactoryvst3.h new file mode 100644 index 000000000..003ef9a18 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/main/pluginfactoryvst3.h @@ -0,0 +1,175 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Common Base Classes +// Filename : public.sdk/source/main/pluginfactoryvst3.h +// Created by : Steinberg, 01/2004 +// Description : Standard Plug-in Factory +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "pluginterfaces/base/ipluginbase.h" + +namespace Steinberg { + +//------------------------------------------------------------------------ +/** Default Class Factory implementation. +\ingroup sdkBase +\see \ref classFactoryMacros */ +//------------------------------------------------------------------------ +class CPluginFactory : public IPluginFactory3 +{ +public: +//------------------------------------------------------------------------ + CPluginFactory (const PFactoryInfo& info); + virtual ~CPluginFactory (); + +//------------------------------------------------------------------------ + /** Registers a Plug-in class with classInfo version 1, returns true for success. */ + bool registerClass (const PClassInfo* info, + FUnknown* (*createFunc)(void*), + void* context = 0); + + /** Registers a Plug-in class with classInfo version 2, returns true for success. */ + bool registerClass (const PClassInfo2* info, + FUnknown* (*createFunc)(void*), + void* context = 0); + + /** Registers a Plug-in class with classInfo Unicode version, returns true for success. */ + bool registerClass (const PClassInfoW* info, + FUnknown* (*createFunc)(void*), + void* context = 0); + + + /** Check if a class for a given classId is already registered. */ + bool isClassRegistered (const FUID& cid); + +//------------------------------------------------------------------------ + DECLARE_FUNKNOWN_METHODS + + //---from IPluginFactory------ + tresult PLUGIN_API getFactoryInfo (PFactoryInfo* info) SMTG_OVERRIDE; + int32 PLUGIN_API countClasses () SMTG_OVERRIDE; + tresult PLUGIN_API getClassInfo (int32 index, PClassInfo* info) SMTG_OVERRIDE; + tresult PLUGIN_API createInstance (FIDString cid, FIDString _iid, void** obj) SMTG_OVERRIDE; + + //---from IPluginFactory2----- + tresult PLUGIN_API getClassInfo2 (int32 index, PClassInfo2* info) SMTG_OVERRIDE; + + //---from IPluginFactory3----- + tresult PLUGIN_API getClassInfoUnicode (int32 index, PClassInfoW* info) SMTG_OVERRIDE; + tresult PLUGIN_API setHostContext (FUnknown* context) SMTG_OVERRIDE; + +//------------------------------------------------------------------------ +protected: + /// @cond + struct PClassEntry + { + //----------------------------------- + PClassInfo2 info8; + PClassInfoW info16; + + FUnknown* (*createFunc)(void*); + void* context; + bool isUnicode; + //----------------------------------- + }; + /// @endcond + + PFactoryInfo factoryInfo; + PClassEntry* classes; + int32 classCount; + int32 maxClassCount; + + bool growClasses (); +}; + +extern CPluginFactory* gPluginFactory; +//------------------------------------------------------------------------ +} // namespace Steinberg + + +//------------------------------------------------------------------------ +#if defined(__GNUC__) && ((__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1))) + #define EXPORT_FACTORY __attribute__ ((visibility ("default"))) +#else + #define EXPORT_FACTORY +#endif + +//------------------------------------------------------------------------ +/** \defgroup classFactoryMacros Macros for defining the class factory +\ingroup sdkBase + +\b Example - How to use the class factory macros: +\code +BEGIN_FACTORY ("Steinberg Technologies", + "http://www.steinberg.de", + "mailto:info@steinberg.de", + PFactoryInfo::kNoFlags) + +DEF_CLASS (INLINE_UID (0x00000000, 0x00000000, 0x00000000, 0x00000000), + PClassInfo::kManyInstances, + "Service", + "Test Service", + TestService::newInstance) + +END_FACTORY +\endcode + +@{*/ + +#define BEGIN_FACTORY(vendor,url,email,flags) using namespace Steinberg; \ + EXPORT_FACTORY IPluginFactory* PLUGIN_API GetPluginFactory () { \ + if (!gPluginFactory) \ + { static PFactoryInfo factoryInfo (vendor,url,email,flags); \ + gPluginFactory = new CPluginFactory (factoryInfo); \ + +#define DEF_CLASS(cid,cardinality,category,name,createMethod) \ + { TUID lcid = cid; static PClassInfo componentClass (lcid,cardinality,category,name); \ + gPluginFactory->registerClass (&componentClass,createMethod); } + +#define DEF_CLASS1(cid,cardinality,category,name,createMethod) \ + { static PClassInfo componentClass (cid,cardinality,category,name); \ + gPluginFactory->registerClass (&componentClass,createMethod); } + +#define DEF_CLASS2(cid,cardinality,category,name,classFlags,subCategories,version,sdkVersion,createMethod) \ + { TUID lcid = cid; static PClassInfo2 componentClass (lcid,cardinality,category,name,classFlags,subCategories, 0 ,version,sdkVersion);\ + gPluginFactory->registerClass (&componentClass,createMethod); } + +#define DEF_CLASS_W(cid,cardinality,category,name,classFlags,subCategories,version,sdkVersion,createMethod) \ + { TUID lcid = cid; static PClassInfoUnicode componentClass (lcid,cardinality,category,name,classFlags,subCategories, 0,version,sdkVersion);\ + gPluginFactory->registerClass (&componentClass,createMethod); } + + +#define END_FACTORY } else gPluginFactory->addRef (); \ + return gPluginFactory; } + +/** @} */ diff --git a/source/includes/vst3sdk/public.sdk/source/main/winexport.def b/source/includes/vst3sdk/public.sdk/source/main/winexport.def new file mode 100644 index 000000000..279a6a5c2 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/main/winexport.def @@ -0,0 +1,4 @@ +EXPORTS + GetPluginFactory + InitDll + ExitDll diff --git a/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/CMakeLists.txt b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/CMakeLists.txt new file mode 100644 index 000000000..54dc60e42 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/CMakeLists.txt @@ -0,0 +1,32 @@ +if(MAC) + if (XCODE AND SMTG_COREAUDIO_SDK_PATH) + set(target auwrapper) + set(${target}_sources + aucarbonview.mm + aucarbonview.h + aucocoaview.mm + aucocoaview.h + ausdk.mm + auwrapper.mm + auwrapper.h + NSDataIBStream.mm + NSDataIBStream.h + ${SDK_ROOT}/pluginterfaces/base/funknown.cpp + ${SDK_ROOT}/pluginterfaces/base/ustring.cpp + ${SDK_ROOT}/public.sdk/source/vst/hosting/eventlist.cpp + ${SDK_ROOT}/public.sdk/source/vst/hosting/eventlist.h + ${SDK_ROOT}/public.sdk/source/vst/hosting/hostclasses.cpp + ${SDK_ROOT}/public.sdk/source/vst/hosting/hostclasses.h + ${SDK_ROOT}/public.sdk/source/vst/hosting/parameterchanges.cpp + ${SDK_ROOT}/public.sdk/source/vst/hosting/parameterchanges.h + ${SDK_ROOT}/public.sdk/source/vst/hosting/processdata.cpp + ${SDK_ROOT}/public.sdk/source/vst/hosting/processdata.h + ) + add_library(${target} STATIC ${${target}_sources}) + target_link_libraries(${target} PRIVATE base "-framework AudioUnit" "-framework CoreMIDI" "-framework AudioToolbox" "-framework CoreFoundation" "-framework Carbon" "-framework Cocoa" "-framework CoreAudio") + target_include_directories(${target} PRIVATE "${SMTG_COREAUDIO_SDK_PATH}/**") + add_custom_command(TARGET ${target} PRE_BUILD COMMAND /usr/bin/ruby "./generateCocoaClassNamePrefix.rb" "${CMAKE_CURRENT_LIST_DIR}" WORKING_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}") + else() + message("* To enable building the AudioUnit wrapper, you need to use the Xcode generator and set SMTG_COREAUDIO_SDK_PATH to the path of your installation of the CoreAudio SDK!") + endif() +endif() \ No newline at end of file diff --git a/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/NSDataIBStream.h b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/NSDataIBStream.h new file mode 100644 index 000000000..89db2df8a --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/NSDataIBStream.h @@ -0,0 +1,92 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/auwrapper/NSDataIBStream.h +// Created by : Steinberg, 12/2007 +// Description : VST 3 -> AU Wrapper +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +/// \cond ignore + +#pragma once + +#import +#import "pluginterfaces/base/ibstream.h" +#import "public.sdk/source/vst/hosting/hostclasses.h" + +//------------------------------------------------------------------------ +namespace Steinberg { +namespace Vst { + +//------------------------------------------------------------------------ +class NSDataIBStream : public IBStream, Vst::IStreamAttributes +{ +public: + NSDataIBStream (NSData* data, bool hideAttributes = false); + virtual ~NSDataIBStream (); + + //---from IBStream------------------- + tresult PLUGIN_API read (void* buffer, int32 numBytes, int32* numBytesRead = 0) SMTG_OVERRIDE; + tresult PLUGIN_API write (void* buffer, int32 numBytes, int32* numBytesWritten = 0) SMTG_OVERRIDE; + tresult PLUGIN_API seek (int64 pos, int32 mode, int64* result = 0) SMTG_OVERRIDE; + tresult PLUGIN_API tell (int64* pos) SMTG_OVERRIDE; + + //---from Vst::IStreamAttributes----- + virtual tresult PLUGIN_API getFileName (String128 name) SMTG_OVERRIDE; + virtual IAttributeList* PLUGIN_API getAttributes () SMTG_OVERRIDE; + + //------------------------------------------------------------------------ + DECLARE_FUNKNOWN_METHODS +protected: + NSData* data; + int64 currentPos; + HostAttributeList attrList; + bool hideAttributes; +}; + +//------------------------------------------------------------------------ +class NSMutableDataIBStream : public NSDataIBStream +{ +public: + NSMutableDataIBStream (NSMutableData* data); + virtual ~NSMutableDataIBStream (); + + tresult PLUGIN_API write (void* buffer, int32 numBytes, int32* numBytesWritten = 0); +//------------------------------------------------------------------------ +protected: + NSMutableData* mdata; +}; + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg + +/// \endcond diff --git a/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/NSDataIBStream.mm b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/NSDataIBStream.mm new file mode 100644 index 000000000..cb85601b8 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/NSDataIBStream.mm @@ -0,0 +1,206 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/auwrapper/NSDataIBStream.mm +// Created by : Steinberg, 12/2007 +// Description : VST 3 -> AU Wrapper +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +/// \cond ignore + +#include "NSDataIBStream.h" + +#include "pluginterfaces/vst/ivstattributes.h" + +#include + +#if __clang__ +#if __has_feature(objc_arc) && __clang_major__ >= 3 +#define ARC_ENABLED 1 +#endif // __has_feature(objc_arc) +#endif // __clang__ + +namespace Steinberg { +namespace Vst { + +DEF_CLASS_IID(IStreamAttributes); + +//------------------------------------------------------------------------ +NSDataIBStream::NSDataIBStream (NSData* data, bool hideAttributes) +: data (data) +, currentPos (0) +, hideAttributes (hideAttributes) +{ + FUNKNOWN_CTOR +#if !ARC_ENABLED + [data retain]; +#endif +} + +//------------------------------------------------------------------------ +NSDataIBStream::~NSDataIBStream () +{ +#if !ARC_ENABLED + [data release]; +#endif + FUNKNOWN_DTOR +} + +//------------------------------------------------------------------------ +IMPLEMENT_REFCOUNT (NSDataIBStream) + +//------------------------------------------------------------------------ +tresult PLUGIN_API NSDataIBStream::queryInterface (const TUID iid, void** obj) +{ + QUERY_INTERFACE (iid, obj, FUnknown::iid, IBStream) + QUERY_INTERFACE (iid, obj, IBStream::iid, IBStream) + if (!hideAttributes) + QUERY_INTERFACE (iid, obj, IStreamAttributes::iid, IStreamAttributes) + *obj = 0; + return kNoInterface; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API NSDataIBStream::read (void* buffer, int32 numBytes, int32* numBytesRead) +{ + int32 useBytes = std::min (numBytes, (int32)([data length] - currentPos)); + if (useBytes > 0) + { + [data getBytes: buffer range: NSMakeRange (currentPos, useBytes)]; + if (numBytesRead) + *numBytesRead = useBytes; + currentPos += useBytes; + return kResultTrue; + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API NSDataIBStream::write (void* buffer, int32 numBytes, int32* numBytesWritten) +{ + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API NSDataIBStream::seek (int64 pos, int32 mode, int64* result) +{ + switch (mode) + { + case kIBSeekSet: + { + if (pos <= [data length]) + { + currentPos = pos; + if (result) + tell (result); + return kResultTrue; + } + break; + } + case kIBSeekCur: + { + if (currentPos + pos <= [data length]) + { + currentPos += pos; + if (result) + tell (result); + return kResultTrue; + } + break; + } + case kIBSeekEnd: + { + if ([data length] + pos <= [data length]) + { + currentPos = [data length] + pos; + if (result) + tell (result); + return kResultTrue; + } + break; + } + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API NSDataIBStream::tell (int64* pos) +{ + if (pos) + { + *pos = currentPos; + return kResultTrue; + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API NSDataIBStream::getFileName (String128 name) +{ + return kNotImplemented; +} + +//------------------------------------------------------------------------ +IAttributeList* PLUGIN_API NSDataIBStream::getAttributes () +{ + return hideAttributes ? 0 : &attrList; +} + +//------------------------------------------------------------------------ +//------------------------------------------------------------------------ +//------------------------------------------------------------------------ +NSMutableDataIBStream::NSMutableDataIBStream (NSMutableData* data) +: NSDataIBStream (data, true) +, mdata (data) +{ +} + +//------------------------------------------------------------------------ +NSMutableDataIBStream::~NSMutableDataIBStream () +{ + [mdata setLength:currentPos]; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API NSMutableDataIBStream::write (void* buffer, int32 numBytes, int32* numBytesWritten) +{ + [mdata replaceBytesInRange:NSMakeRange (currentPos, numBytes) withBytes:buffer]; + if (numBytesWritten) + *numBytesWritten = numBytes; + currentPos += numBytes; + return kResultTrue; +} + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg + +/// \endcond diff --git a/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/again/CMakeLists.txt b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/again/CMakeLists.txt new file mode 100644 index 000000000..18b36a7b1 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/again/CMakeLists.txt @@ -0,0 +1,41 @@ +if(MAC AND XCODE AND SMTG_COREAUDIO_SDK_PATH) + set(target again_au) + set(${target}_sources + doc.cpp + audiounitconfig.h + Info.plist + ) + add_library(${target} MODULE ${${target}_sources}) + set_target_properties(${target} PROPERTIES BUNDLE TRUE) + set_target_properties(${target} PROPERTIES XCODE_ATTRIBUTE_GENERATE_MASTER_OBJECT_FILE "YES") + set_target_properties(${target} PROPERTIES XCODE_ATTRIBUTE_OTHER_LDFLAGS "-all_load") + set_target_properties(${target} PROPERTIES XCODE_ATTRIBUTE_GENERATE_PKGINFO_FILE "YES") + set_target_properties(${target} PROPERTIES XCODE_ATTRIBUTE_WRAPPER_EXTENSION "component") + set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${VST3_OUTPUT_DIR}) + target_link_libraries(${target} PRIVATE auwrapper) + smtg_set_bundle(${target} INFOPLIST "${CMAKE_CURRENT_LIST_DIR}/Info.plist" PREPROCESS) + + set(outputdir ${VST3_OUTPUT_DIR}/$) + + add_dependencies(${target} again) + add_custom_command(TARGET ${target} POST_BUILD COMMAND /bin/mkdir "-p" ${outputdir}/${target}.component/Contents/Resources) + add_custom_command(TARGET ${target} POST_BUILD COMMAND /bin/ln "-sf" "${outputdir}/again.vst3" "${outputdir}/${target}.component/Contents/Resources/plugin.vst3") + add_custom_command(TARGET ${target} POST_BUILD COMMAND /bin/ln "-sf" "${outputdir}/${target}.component" "~/Library/Audio/Plug-Ins/Components/") + + execute_process(COMMAND xcrun --find Rez OUTPUT_VARIABLE OSX_REZ_COMMAND OUTPUT_STRIP_TRAILING_WHITESPACE) + add_custom_command(TARGET ${target} POST_BUILD COMMAND "${OSX_REZ_COMMAND}" + "-d" "SystemSevenOrLater=1" + "-script" "Roman" + "-d" "i386_YES" + "-d" "x86_64_YES" + "-is" "${CMAKE_OSX_SYSROOT}" + "-I" "${CMAKE_OSX_SYSROOT}/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework/Versions/A/Headers" + "-I" "/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework/Versions/A/Headers" + "-I" "${SMTG_COREAUDIO_SDK_PATH}/AudioUnits/AUPublic/AUBase" + "-I" "${CMAKE_CURRENT_LIST_DIR}" + "-o" "${outputdir}/${target}.component/Contents/Resources/again_au.rsrc" + "-useDF" + "${CMAKE_CURRENT_LIST_DIR}/../auresource.r" + ) + +endif() diff --git a/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/again/Info.plist b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/again/Info.plist new file mode 100644 index 000000000..8c7adad4d --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/again/Info.plist @@ -0,0 +1,117 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/auwrapper/again/Info.plist +// Created by : Steinberg, 12/2007 +// Description : VST 3 -> AU Wrapper +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "audiounitconfig.h" + + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + kAudioUnitBundleIdentifier + CFBundleName + $(PRODUCT_NAME) + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + CSResourcesFileMapped + yes + + /* + The "AudioUnit SupportedNumChannels" key is only necessary if you build an effect not an instrument. + You should describe all supported channel configurations. + This example says that it supports Stereo/Stereo or Mono/Mono. + */ + + AudioUnit SupportedNumChannels + + + Outputs + 2 + Inputs + 2 + + + Outputs + 0 + Inputs + 1 + + + Outputs + 1 + Inputs + 1 + + + + AudioUnit Version + kAudioUnitVersion + + /* + Support for the new AUPlugIn model in Mac OS X 10.7 + */ + AudioComponents + + + description + kAUPluginDescription + factoryFunction + AUWrapperFactory + manufacturer + kAUPluginManufacturer + name + kAUPluginName + subtype + kAUPluginSubType + type + kAUPluginType + version + kAudioUnitVersion + + + + + diff --git a/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/again/audiounitconfig.h b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/again/audiounitconfig.h new file mode 100644 index 000000000..a242baecd --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/again/audiounitconfig.h @@ -0,0 +1,61 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/auwrapper/again/audiounitconfig.h +// Created by : Steinberg, 12/2007 +// Description : VST 3 -> AU Wrapper +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +// AUWRAPPER_CHANGE + +/* Bundle Identifier */ +#define kAudioUnitBundleIdentifier com.steinberg.vst3plugin.again.audiounit +/* Version Number (needs to be in hex)*/ +#define kAudioUnitVersion 0xFFFFFFFF +/* Company Name + Effect Name */ +#define kAUPluginName Steinberg: AGain +/* Effect Description */ +#define kAUPluginDescription AGain +/* Audio Unit Type */ +#define kAUPluginType aufx +/* Unique ID */ +#define kAUPluginSubType gain +/* Registered Company ID */ +#define kAUPluginManufacturer Stgb + +// Definitions for the resource file +#define kAudioUnitName "Steinberg: AGain" // same as kAUPluginName +#define kAudioUnitDescription "AGain" // same as kAUPluginDescription +#define kAudioUnitType 'aufx' //kAudioUnitType_Effect // same as kAUPluginType +#define kAudioUnitComponentSubType 'gain' // same as kAUPluginSubType +#define kAudioUnitComponentManuf 'Stgb' // same as kAUPluginManufacturer + +#define kAudioUnitCarbonView 1 // if 0 no Carbon view support will be added diff --git a/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/again/config/again.xcconfig b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/again/config/again.xcconfig new file mode 100644 index 000000000..306220cfa --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/again/config/again.xcconfig @@ -0,0 +1,51 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : again.xcconfig +// Created by : Steinberg, 5/24/12 +// Description : Xcode configuration file, VST 3 -> AU Wrapper +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "../../config/ausdkpath" // AUWRAPPER_CHANGE + +PRODUCT_NAME = again // AUWRAPPER_CHANGE +WRAPPER_EXTENSION = component + +STRIP_STYLE = non-global +OTHER_LDFLAGS = -all_load +GENERATE_MASTER_OBJECT_FILE = YES +KEEP_PRIVATE_EXTERNS = YES +SEPARATE_SYMBOL_EDIT = YES +OTHER_REZFLAGS = -d ppc_$ppc -d i386_$i386 -d ppc64_$ppc64 -d x86_64_$x86_64 -I /System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework/Versions/A/Headers -I /$DEVELOPER_DIR/Examples/CoreAudio/AudioUnits/AUPublic/AUBase -I /$DEVELOPER_DIR/Extras/CoreAudio/AudioUnits/AUPublic/AUBase -I $(CUSTOM_AU_SDK_PATH)/AudioUnits/AUPublic/AUBase +INFOPLIST_PREPROCESS = YES +PRESERVE_DEAD_CODE_INITS_AND_TERMS = YES +DEBUG_INFORMATION_FORMAT = dwarf-with-dsym +INSTALL_PATH = /Library/Audio/Plug-Ins/Components/ diff --git a/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/again/config/again_debug.xcconfig b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/again/config/again_debug.xcconfig new file mode 100644 index 000000000..07bb23f04 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/again/config/again_debug.xcconfig @@ -0,0 +1,38 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : again_debug.xcconfig +// Created by : Steinberg, 5/24/12 +// Description : Xcode configuration file, VST 3 -> AU Wrapper +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "../../../../../base/mac/config/debug" // AUWRAPPER_CHANGE +#include "again" diff --git a/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/again/config/again_release.xcconfig b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/again/config/again_release.xcconfig new file mode 100644 index 000000000..607c76ea1 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/again/config/again_release.xcconfig @@ -0,0 +1,38 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : again_release.xcconfig +// Created by : Steinberg, 5/24/12 +// Description : Xcode configuration file, VST 3 -> AU Wrapper +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "../../../../../base/mac/config/release" // AUWRAPPER_CHANGE +#include "again" diff --git a/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/again/doc.cpp b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/again/doc.cpp new file mode 100644 index 000000000..15690be19 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/again/doc.cpp @@ -0,0 +1,52 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/auwrapper/again/doc.cpp +// Created by : Steinberg, 12/2007 +// Description : VST 3 -> AU Wrapper +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +/* + + TODO: cmake adaption needed + + How-To use the VST3->AU Wrapper: + + - Make a copy of this project + - search trough the project files for "AUWRAPPER_CHANGE". You may need to change the stuff there especially + if you have moved the project so that some paths are invalid + - edit audiounitconfig.h see comments there + - edit Info.plist see comments there + - edit the "Make Links Script" for easier debugging/development + + - For the release version, you must place a copy or an alias of your vst3 plugin in the resource folder of the bundle named "plugin.vst3" + + */ diff --git a/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/aucarbonview.h b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/aucarbonview.h new file mode 100644 index 000000000..e21ce3a4e --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/aucarbonview.h @@ -0,0 +1,83 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/auwrapper/aucarbonview.h +// Created by : Steinberg, 12/2007 +// Description : VST 3 -> AU Wrapper +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +/// \cond ignore + +#pragma once + +#if !__LP64__ + +#include "AUPublic/AUCarbonViewBase/AUCarbonViewBase.h" +#include "pluginterfaces/vst/ivsteditcontroller.h" +#include "base/source/fobject.h" +#include "pluginterfaces/gui/iplugview.h" + +namespace Steinberg { +namespace Vst { +class AUCarbonPlugFrame; + +//------------------------------------------------------------------------ +class AUCarbonView : public AUCarbonViewBase, public IPlugFrame, public FObject +{ +public: + AUCarbonView (AudioUnitCarbonView auv); + ~AUCarbonView (); + + OSStatus CreateUI (Float32 xoffset, Float32 yoffset); + + OBJ_METHODS(AUCarbonView, FObject) + DEF_INTERFACES_1(IPlugFrame, FObject) + REFCOUNT_METHODS(FObject) + +protected: + tresult PLUGIN_API resizeView (IPlugView* view, ViewRect* vr); + + static OSStatus HIViewAdded (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void* inUserData); + + IEditController* editController; + AUCarbonPlugFrame* plugFrame; + IPlugView* plugView; + HIViewRef hiPlugView; + EventHandlerRef eventHandler; +}; + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg + +#endif // !__LP64__ + +/// \endcond diff --git a/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/aucarbonview.mm b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/aucarbonview.mm new file mode 100644 index 000000000..d59f88cfb --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/aucarbonview.mm @@ -0,0 +1,167 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/auwrapper/aucarbonview.mm +// Created by : Steinberg, 12/2007 +// Description : VST 3 -> AU Wrapper +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +/// \cond ignore + +#include "aucarbonview.h" + +#if !__LP64__ + +namespace Steinberg { +namespace Vst { + +//------------------------------------------------------------------------ +AUCarbonView::AUCarbonView (AudioUnitCarbonView auv) +: AUCarbonViewBase (auv) +, editController (0) +, plugView (0) +, hiPlugView (0) +{ +} + +//------------------------------------------------------------------------ +AUCarbonView::~AUCarbonView () +{ + if (plugView) + { + plugView->setFrame (0); + plugView->removed (); + plugView->release (); + } +} + +//------------------------------------------------------------------------ +OSStatus AUCarbonView::HIViewAdded (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData) +{ + UInt32 eventClass = GetEventClass (inEvent); + UInt32 eventKind = GetEventKind (inEvent); + if (eventClass == kEventClassControl && eventKind == kEventControlAddedSubControl) + { + HIViewRef newControl; + if (GetEventParameter (inEvent, kEventParamControlSubControl, typeControlRef, NULL, sizeof (HIViewRef) , NULL , &newControl) == noErr) + { + AUCarbonView* wrapper = (AUCarbonView*)inUserData; + wrapper->hiPlugView = newControl; + RemoveEventHandler (wrapper->eventHandler); + wrapper->eventHandler = 0; + } + } + return eventNotHandledErr; +} + +//------------------------------------------------------------------------ +OSStatus AUCarbonView::CreateUI (Float32 xoffset, Float32 yoffset) +{ + AudioUnit unit = GetEditAudioUnit (); + if (unit) + { + if (!editController) + { + UInt32 size = sizeof (IEditController*); + if (AudioUnitGetProperty (unit, 64000, kAudioUnitScope_Global, 0, &editController, &size) != noErr) + return kAudioUnitErr_NoConnection; + } + if (editController) + { + plugView = editController->createView (ViewType::kEditor); + if (!plugView) + return kAudioUnitErr_NoConnection; + + HIViewRef contentView; + const EventTypeSpec eventTypes[] = { + { kEventClassControl, kEventControlAddedSubControl }, + }; + OSStatus err = HIViewFindByID (HIViewGetRoot (GetCarbonWindow ()), kHIViewWindowContentID, &contentView); + err = InstallControlEventHandler (contentView, HIViewAdded, 1, eventTypes, this, &eventHandler); + + plugView->setFrame (this); + + if (plugView->attached (GetCarbonWindow (), kPlatformTypeHIView) == kResultTrue) + { + HIViewRemoveFromSuperview (hiPlugView); + EmbedControl (hiPlugView); + HIViewMoveBy (hiPlugView, xoffset, yoffset); + return noErr; + } + else + plugView->setFrame (0); + } + } + return kAudioUnitErr_NoConnection; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API AUCarbonView::resizeView (IPlugView* view, ViewRect* vr) +{ + if (vr == 0 || view != plugView) + return kInvalidArgument; + + HIViewRef hiView = GetCarbonPane (); + if (hiView) + { + HIRect r; + if (HIViewGetFrame (hiView, &r) != noErr) + return kResultFalse; + r.size.width = vr->right - vr->left; + r.size.height = vr->bottom - vr->top; + if (HIViewSetFrame (hiView, &r) != noErr) + return kResultFalse; + + if (plugView) + plugView->onSize (vr); + + return kResultTrue; + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +//COMPONENT_ENTRY(AUCarbonView) +//------------------------------------------------------------------------ +extern "C" { + ComponentResult AUCarbonViewEntry(ComponentParameters *params, AUCarbonView *obj); + __attribute__ ((visibility ("default"))) ComponentResult AUCarbonViewEntry(ComponentParameters *params, AUCarbonView *obj) + { + return ComponentEntryPoint::Dispatch(params, obj); + } +} + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg +#endif + +/// \endcond diff --git a/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/aucocoaview.h b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/aucocoaview.h new file mode 100644 index 000000000..f9be908d4 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/aucocoaview.h @@ -0,0 +1,68 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/auwrapper/aucocoaview.h +// Created by : Steinberg, 12/2007 +// Description : VST 3 -> AU Wrapper +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +/// \cond ignore + +#pragma once + +#import "aucocoaclassprefix.h" + +#import +#import + +#ifndef SMTG_AU_NAMESPACE +# error define SMTG_AU_NAMESPACE +#endif + +//----------------------------------------------------------------------------- +#define SMTG_AU_PLUGIN_NAMESPACE0(x) x +#define SMTG_AU_PLUGIN_NAMESPACE1(a, b) a##_##b +#define SMTG_AU_PLUGIN_NAMESPACE2(a, b) SMTG_AU_PLUGIN_NAMESPACE1(a,b) +#define SMTG_AU_PLUGIN_NAMESPACE(name) SMTG_AU_PLUGIN_NAMESPACE2(SMTG_AU_PLUGIN_NAMESPACE0(name), SMTG_AU_PLUGIN_NAMESPACE0(SMTG_AU_NAMESPACE)) + +//----------------------------------------------------------------------------- +// SMTG_AU_PLUGIN_NAMESPACE (SMTGAUPluginCocoaView) +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +@interface SMTG_AU_PLUGIN_NAMESPACE (SMTGAUPluginCocoaView) : NSObject +{ +} + +//----------------------------------------------------------------------------- +@end + +/// \endcond diff --git a/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/aucocoaview.mm b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/aucocoaview.mm new file mode 100644 index 000000000..01affc247 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/aucocoaview.mm @@ -0,0 +1,232 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/auwrapper/aucocoaview.mm +// Created by : Steinberg, 12/2007 +// Description : VST 3 -> AU Wrapper +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +/// \cond ignore + +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + +#import "aucocoaview.h" +#import "auwrapper.h" +#import "pluginterfaces/vst/ivsteditcontroller.h" +#import "pluginterfaces/gui/iplugview.h" +#import "base/source/fobject.h" + +namespace Steinberg { +DEF_CLASS_IID(IPlugFrame) + +//------------------------------------------------------------------------ +class AUPlugFrame : public FObject, public IPlugFrame +//------------------------------------------------------------------------ +{ +public: + AUPlugFrame (NSView* parent) : parent (parent) {} + tresult PLUGIN_API resizeView (IPlugView* view, ViewRect* vr) SMTG_OVERRIDE + { + NSRect newSize = NSMakeRect ([parent frame].origin.x, [parent frame].origin.y, vr->right - vr->left, vr->bottom - vr->top); + [parent setFrame:newSize]; + return kResultTrue; + } + + OBJ_METHODS(AUPlugFrame, FObject) + DEF_INTERFACES_1(IPlugFrame, FObject) + REFCOUNT_METHODS(FObject) +protected: + NSView* parent; +}; + +} // namespace Steinberg + +using namespace Steinberg; + +//------------------------------------------------------------------------ +@interface SMTGCocoa_NSViewWrapperForAU : NSView { +//------------------------------------------------------------------------ + IPlugView* plugView; + Vst::IEditController* editController; + AudioUnit audioUnit; + FObject* dynlib; + AUPlugFrame* plugFrame; + + BOOL isAttached; +} + +- (id) initWithEditController: (Vst::IEditController*) editController audioUnit: (AudioUnit) au preferredSize: (NSSize) size; + +@end + +//------------------------------------------------------------------------ +// SMTG_AU_PLUGIN_NAMESPACE (SMTGAUPluginCocoaView) +//------------------------------------------------------------------------ + +//------------------------------------------------------------------------ +@implementation SMTG_AU_PLUGIN_NAMESPACE (SMTGAUPluginCocoaView) + +//------------------------------------------------------------------------ +- (unsigned) interfaceVersion +{ + return 0; +} + +//------------------------------------------------------------------------ +- (NSString *) description +{ + return @"Cocoa View"; +} + +//------------------------------------------------------------------------ +- (NSView *)uiViewForAudioUnit:(AudioUnit)inAU withSize:(NSSize)inPreferredSize +{ + Vst::IEditController* editController = 0; + UInt32 size = sizeof (Vst::IEditController*); + if (AudioUnitGetProperty (inAU, 64000, kAudioUnitScope_Global, 0, &editController, &size) != noErr) + return nil; + return [[[SMTGCocoa_NSViewWrapperForAU alloc] initWithEditController:editController audioUnit:inAU preferredSize:inPreferredSize] autorelease]; +} + +@end + +//------------------------------------------------------------------------ +@implementation SMTGCocoa_NSViewWrapperForAU +//------------------------------------------------------------------------ +- (id) initWithEditController: (Vst::IEditController*) _editController audioUnit: (AudioUnit) au preferredSize: (NSSize) size +{ + self = [super initWithFrame: NSMakeRect (0, 0, size.width, size.height)]; + if (self) + { + editController = _editController; + editController->addRef (); + audioUnit = au; + plugView = editController->createView (Vst::ViewType::kEditor); + if (!plugView || plugView->isPlatformTypeSupported (kPlatformTypeNSView) != kResultTrue) + { + [self dealloc]; + return nil; + } + + plugFrame = NEW AUPlugFrame (self); + plugView->setFrame (plugFrame); + + if (plugView->attached (self, kPlatformTypeNSView) != kResultTrue) + { + [self dealloc]; + return nil; + } + ViewRect vr; + if (plugView->getSize (&vr) == kResultTrue) + { + NSRect newSize = NSMakeRect (0, 0, vr.right - vr.left, vr.bottom - vr.top); + [self setFrame:newSize]; + } + + isAttached = YES; + UInt32 size = sizeof (FObject*); + if (AudioUnitGetProperty (audioUnit, 64001, kAudioUnitScope_Global, 0, &dynlib, &size) == noErr) + dynlib->addRef (); + } + return self; +} + + +//------------------------------------------------------------------------ +- (void) setFrame: (NSRect) newSize +{ + [super setFrame: newSize]; + ViewRect viewRect (0, 0, newSize.size.width, newSize.size.height); + + if (plugView) + plugView->onSize (&viewRect); +} + + +//------------------------------------------------------------------------ +- (BOOL)isFlipped { return YES; } + +//------------------------------------------------------------------------ +- (void)viewDidMoveToSuperview +{ + if (plugView) + { + if ([self superview]) + { + if (!isAttached) + { + isAttached = plugView->attached (self, kPlatformTypeNSView) == kResultTrue; + } + } + else + { + if (isAttached) + { + plugView->removed (); + isAttached = NO; + } + } + } +} + +//------------------------------------------------------------------------ +- (void) dealloc +{ + if (plugView) + { + if (isAttached) + { + plugView->setFrame (0); + plugView->removed (); + } + plugView->release (); + if (plugFrame) + plugFrame->release (); + + if (editController) + { + Steinberg::uint32 refCount= editController->addRef (); + if (refCount == 2) + editController->terminate (); + + editController->release (); + editController->release (); + editController = 0; + } + } + if (dynlib) + dynlib->release (); + [super dealloc]; +} + +@end + +/// \endcond diff --git a/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/auresource.r b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/auresource.r new file mode 100644 index 000000000..146cfe771 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/auresource.r @@ -0,0 +1,85 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/auwrapper/auresource.r +// Created by : Steinberg, 12/2007 +// Description : VST 3 -> AU Wrapper +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include +#include + +#include "audiounitconfig.h" +/* ---------------------------------------------------------------------------------------------------------------------------------------- +// audiounitconfig.h needs the following definitions: + #define kAudioUnitVersion 0xFFFFFFFF // Version Number, needs to be in hex + #define kAudioUnitName "Steinberg: MyVST3 as AudioUnit" // Company Name + Effect Name + #define kAudioUnitDescription "My VST3 as AudioUnit" // Effect Description + #define kAudioUnitType kAudioUnitType_Effect // can be kAudioUnitType_Effect or kAudioUnitType_MusicDevice + #define kAudioUnitComponentSubType 'test' // unique id + #define kAudioUnitComponentManuf 'SMTG' // registered company id + #define kAudioUnitCarbonView 1 // if 0 no Carbon view support will be added +*/ + + +#define kAudioUnitResID_Processor 1000 +#define kAudioUnitResID_CarbonView 9000 + +//----------------------Processor---------------------------------------------- + +#define RES_ID kAudioUnitResID_Processor +#define COMP_TYPE kAudioUnitType +#define COMP_SUBTYPE kAudioUnitComponentSubType +#define COMP_MANUF kAudioUnitComponentManuf + +#define VERSION kAudioUnitVersion +#define NAME kAudioUnitName +#define DESCRIPTION kAudioUnitDescription +#define ENTRY_POINT "AUWrapperEntry" + +#include "AUResources.r" + +#if kAudioUnitCarbonView +//----------------------View---------------------------------------------- + +#define RES_ID kAudioUnitResID_CarbonView +#define COMP_TYPE kAudioUnitCarbonViewComponentType +#define COMP_SUBTYPE kAudioUnitComponentSubType +#define COMP_MANUF kAudioUnitComponentManuf + +#define VERSION kAudioUnitVersion +#define NAME "CarbonView" +#define DESCRIPTION "CarbonView" +#define ENTRY_POINT "AUCarbonViewEntry" + +#include "AUResources.r" + +#endif diff --git a/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/ausdk.mm b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/ausdk.mm new file mode 100644 index 000000000..eeef69a5f --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/ausdk.mm @@ -0,0 +1,84 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/auwrapper/ausdk.mm +// Created by : Steinberg, 12/2007 +// Description : VST 3 -> AU Wrapper +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +/// \cond ignore + +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + +#ifndef MAC_OS_X_VERSION_10_7 + #define MAC_OS_X_VERSION_10_7 1070 +#endif + +#import "PublicUtility/CAAudioChannelLayout.cpp" +#import "PublicUtility/CABundleLocker.cpp" +#import "PublicUtility/CAHostTimeBase.cpp" +#import "PublicUtility/CAStreamBasicDescription.cpp" +#import "PublicUtility/CAVectorUnit.cpp" +#import "PublicUtility/CAAUParameter.cpp" + +#import "AUPublic/AUBase/ComponentBase.cpp" +#import "AUPublic/AUBase/AUScopeElement.cpp" +#import "AUPublic/AUBase/AUOutputElement.cpp" +#import "AUPublic/AUBase/AUInputElement.cpp" +#import "AUPublic/AUBase/AUBase.cpp" + +#if !__LP64__ + #import "AUPublic/AUCarbonViewBase/AUCarbonViewBase.cpp" + #import "AUPublic/AUCarbonViewBase/AUCarbonViewControl.cpp" + #import "AUPublic/AUCarbonViewBase/AUCarbonViewDispatch.cpp" + #import "AUPublic/AUCarbonViewBase/AUControlGroup.cpp" + #import "AUPublic/AUCarbonViewBase/CarbonEventHandler.cpp" +#endif + +#import "AUPublic/Utility/AUTimestampGenerator.cpp" +#import "AUPublic/Utility/AUBuffer.cpp" +#import "AUPublic/Utility/AUBaseHelper.cpp" + +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 + #import "AUPublic/OtherBases/AUMIDIEffectBase.cpp" + #import "AUPublic/Utility/AUDebugDispatcher.cpp" +#else + #import "AUPublic/AUBase/AUPlugInDispatch.cpp" +#endif + +#if !CA_USE_AUDIO_PLUGIN_ONLY + #import "AUPublic/AUBase/AUDispatch.cpp" + #import "AUPublic/OtherBases/MusicDeviceBase.cpp" + #import "AUPublic/OtherBases/AUMIDIBase.cpp" + #import "AUPublic/OtherBases/AUEffectBase.cpp" +#endif + +/// \endcond diff --git a/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/auwrapper.h b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/auwrapper.h new file mode 100644 index 000000000..0c6da3e94 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/auwrapper.h @@ -0,0 +1,340 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/auwrapper/auwrapper.h +// Created by : Steinberg, 12/2007 +// Description : VST 3 -> AU Wrapper +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +/** +*************************** +\page auwrapper AudioUnit Wrapper +*************************** +\section AUIntroduction Introduction +*************************** +The VST 3 SDK comes with an AudioUnit wrapper, which can wrap one VST 3 Audio Processor and Edit Controller as an AudioUnit effect/instrument. + +The wrapper is a small dynamic library which loads the VST 3 Plug-in. +As AudioUnits store some important information in their resource fork, this library must be compiled for every VST 3 Plug-in. +\n\n +*************************** +\section AUhowdoesitwork How does it work ? +*************************** + +- build the auwrapper project (public.sdk/source/vst/auwrapper/auwrapper.xcodeproj) +- create a copy of the again AU wrapper example project directory (public.sdk/source/vst/auwrapper/again/) +- rename the copy to your needs +- edit the target settings of the project and change + - Product Name + - Library search path so that it points to the directory where libauwrapper.a exists + - architecture setting so that it only includes architectures the VST 3 Plug-in supports + +- search in the project for AUWRAPPER_CHANGE and change the settings to your needs, especially in : + - edit audiounitconfig.h see comments there + - edit Info.plist see comments there +- edit the "Make Links Script" for easier debugging/development +- build your project +- done... that is all! + +For the release version, you must place a copy or an alias of your VST 3 Plug-in in the resource folder of the bundle named "plugin.vst3" + + */ +/// \cond ignore + +#pragma once + +#if CA_USE_AUDIO_PLUGIN_ONLY +#include "AudioUnits/AUPublic/AUBase/AUBase.h" +#define AUWRAPPER_BASE_CLASS AUBase +#else +#include "AudioUnits/AUPublic/OtherBases/MusicDeviceBase.h" +#define AUWRAPPER_BASE_CLASS MusicDeviceBase +#endif +#include "pluginterfaces/vst/ivstaudioprocessor.h" +#include "pluginterfaces/vst/ivsteditcontroller.h" +#include "pluginterfaces/vst/ivstprocesscontext.h" +#include "pluginterfaces/vst/ivstunits.h" +#include "public.sdk/source/vst/hosting/parameterchanges.h" +#include "public.sdk/source/vst/hosting/processdata.h" +#include "public.sdk/source/vst/hosting/eventlist.h" +#include "base/source/timer.h" +#include "base/source/fstring.h" +#include "base/source/flock.h" +#include +#include +#include +#include + +namespace Steinberg { +namespace Vst { + +//------------------------------------------------------------------------ +typedef struct MIDIMessageInfoStruct { + UInt8 status; + UInt8 channel; + UInt8 data1; + UInt8 data2; + UInt32 startFrame; +} MIDIMessageInfoStruct; + +//------------------------------------------------------------------------ +class MIDIOutputCallbackHelper +{ +public: + MIDIOutputCallbackHelper () + { + mMIDIMessageList.reserve (16); + mMIDICallbackStruct.midiOutputCallback = NULL; + } + virtual ~MIDIOutputCallbackHelper () {}; + + void SetCallbackInfo (AUMIDIOutputCallback callback, void* userData) + { + mMIDICallbackStruct.midiOutputCallback = callback; + mMIDICallbackStruct.userData = userData; + } + + void AddEvent (UInt8 status, UInt8 channel, UInt8 data1, UInt8 data2, UInt32 inStartFrame) + { + MIDIMessageInfoStruct info = {status, channel, data1, data2, inStartFrame}; + mMIDIMessageList.push_back (info); + } + + void FireAtTimeStamp (const AudioTimeStamp& inTimeStamp) + { + if (!mMIDIMessageList.empty () && mMIDICallbackStruct.midiOutputCallback != 0) + { + // synthesize the packet list and call the MIDIOutputCallback + // iterate through the vector and get each item + std::vector::iterator myIterator; + MIDIPacketList* pktlist = PacketList (); + + for (myIterator = mMIDIMessageList.begin (); myIterator != mMIDIMessageList.end (); myIterator++) + { + MIDIMessageInfoStruct item = *myIterator; + + MIDIPacket* pkt = MIDIPacketListInit (pktlist); + bool tooBig = false; + Byte data[3] = { item.status, item.data1, item.data2 }; + if ((pkt = MIDIPacketListAdd (pktlist, sizeof (mBuffersAllocated), pkt, item.startFrame, 4, const_cast(data))) == NULL) + tooBig = true; + + if (tooBig) + { // send what we have and then clear the buffer and send again + // issue the callback with what we got + OSStatus result = mMIDICallbackStruct.midiOutputCallback (mMIDICallbackStruct.userData, &inTimeStamp, 0, pktlist); + if (result != noErr) + printf ("error calling output callback: %d", (int) result); + + // clear stuff we've already processed, and fire again + mMIDIMessageList.erase (mMIDIMessageList.begin (), myIterator); + this->FireAtTimeStamp (inTimeStamp); + return; + } + } + // fire callback + OSStatus result = mMIDICallbackStruct.midiOutputCallback (mMIDICallbackStruct.userData, &inTimeStamp, 0, pktlist); + if (result != noErr) + printf ("error calling output callback: %d", (int) result); + + mMIDIMessageList.clear (); + } + } + +protected: + typedef std::vector MIDIMessageList; + +private: + MIDIPacketList* PacketList () {return (MIDIPacketList*)mBuffersAllocated;} + + Byte mBuffersAllocated[1024]; + AUMIDIOutputCallbackStruct mMIDICallbackStruct; + MIDIMessageList mMIDIMessageList; +}; + + +//------------------------------------------------------------------------ +//------------------------------------------------------------------------ +class AUWrapper +: public AUWRAPPER_BASE_CLASS +, public IComponentHandler +, public ITimerCallback +{ +public: + AUWrapper (ComponentInstanceRecord* ci); + ~AUWrapper (); + + //---ComponentBase--------------------- + ComponentResult Version () SMTG_OVERRIDE; + void PostConstructor () SMTG_OVERRIDE; + + //---AUBase----------------------------- + void Cleanup () SMTG_OVERRIDE; + ComponentResult Initialize () SMTG_OVERRIDE; + AUElement* CreateElement (AudioUnitScope scope, AudioUnitElement element) SMTG_OVERRIDE; + UInt32 SupportedNumChannels (const AUChannelInfo** outInfo) SMTG_OVERRIDE; + bool StreamFormatWritable (AudioUnitScope scope, AudioUnitElement element) SMTG_OVERRIDE; + ComponentResult ChangeStreamFormat (AudioUnitScope inScope, AudioUnitElement inElement, const CAStreamBasicDescription& inPrevFormat, const CAStreamBasicDescription& inNewFormat) SMTG_OVERRIDE; + ComponentResult SetConnection (const AudioUnitConnection& inConnection) SMTG_OVERRIDE; + ComponentResult GetParameterInfo (AudioUnitScope inScope, AudioUnitParameterID inParameterID, AudioUnitParameterInfo& outParameterInfo) SMTG_OVERRIDE; + ComponentResult SetParameter (AudioUnitParameterID inID, AudioUnitScope inScope, AudioUnitElement inElement, AudioUnitParameterValue inValue, UInt32 inBufferOffsetInFrames) SMTG_OVERRIDE; + + ComponentResult SaveState (CFPropertyListRef* outData) SMTG_OVERRIDE; + ComponentResult RestoreState (CFPropertyListRef inData) SMTG_OVERRIDE; + + ComponentResult Render (AudioUnitRenderActionFlags &ioActionFlags, const AudioTimeStamp &inTimeStamp, UInt32 inNumberFrames) SMTG_OVERRIDE; + void processOutputEvents (const AudioTimeStamp &inTimeStamp); + + int GetNumCustomUIComponents () SMTG_OVERRIDE; + void GetUIComponentDescs (ComponentDescription* inDescArray) SMTG_OVERRIDE; + + ComponentResult GetPropertyInfo (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32 &outDataSize, Boolean &outWritable) SMTG_OVERRIDE; + ComponentResult GetProperty (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData) SMTG_OVERRIDE; + ComponentResult SetProperty (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize) SMTG_OVERRIDE; + bool CanScheduleParameters() const; // Not in the base class anymore in newer CoreAudio SDKs + + Float64 GetLatency () SMTG_OVERRIDE; + Float64 GetTailTime () SMTG_OVERRIDE; + + //---Factory presets + OSStatus GetPresets (CFArrayRef* outData) const SMTG_OVERRIDE; + OSStatus NewFactoryPresetSet (const AUPreset& inNewFactoryPreset) SMTG_OVERRIDE; + + //---MusicDeviceBase------------------------- + ComponentResult StartNote (MusicDeviceInstrumentID inInstrument, MusicDeviceGroupID inGroupID, NoteInstanceID* outNoteInstanceID, UInt32 inOffsetSampleFrame, const MusicDeviceNoteParams &inParams) SMTG_OVERRIDE; + ComponentResult StopNote (MusicDeviceGroupID inGroupID, NoteInstanceID inNoteInstanceID, UInt32 inOffsetSampleFrame) SMTG_OVERRIDE; + OSStatus GetInstrumentCount (UInt32 &outInstCount) const SMTG_OVERRIDE; + + //---AUMIDIBase------------------------------ + OSStatus HandleNonNoteEvent (UInt8 status, UInt8 channel, UInt8 data1, UInt8 data2, UInt32 inStartFrame) SMTG_OVERRIDE; + + //---custom---------------------------------- + void setControllerParameter (ParamID pid, ParamValue value); + + // return for a given midiChannel the unitID and the ProgramListID + bool getProgramListAndUnit (int32 midiChannel, UnitID& unitId, ProgramListID& programListId); + + // restore preset state, add StateType "Project" to stream if loading from project + ComponentResult restoreState (CFPropertyListRef inData, bool fromProject); + + //------------------------------------------------------------------------ +#if !CA_USE_AUDIO_PLUGIN_ONLY + static ComponentResult ComponentEntryDispatch (ComponentParameters* params, AUWrapper* This); +#endif + //------------------------------------------------------------------------ + static CFBundleRef gBundleRef; + //------------------------------------------------------------------------ + DECLARE_FUNKNOWN_METHODS + +protected: + //---from IComponentHandler------------------- + tresult PLUGIN_API beginEdit (ParamID tag) SMTG_OVERRIDE; + tresult PLUGIN_API performEdit (ParamID tag, ParamValue valueNormalized) SMTG_OVERRIDE; + tresult PLUGIN_API endEdit (ParamID tag) SMTG_OVERRIDE; + tresult PLUGIN_API restartComponent (int32 flags) SMTG_OVERRIDE; + + //---from ITimerCallback---------------------- + void onTimer (Timer* timer) SMTG_OVERRIDE; + + // internal helpers + double getSampleRate () const { return sampleRate; } + void updateProcessContext (); + void syncParameterValues (); + void cacheParameterValues (); + void clearParameterValueCache (); + + virtual IPluginFactory* getFactory (); + void loadVST3Module (); + void unloadVST3Module (); + bool validateChannelPair (int inChannelsIn, int inChannelsOut, const AUChannelInfo* info, UInt32 numChanInfo) const; + + + IAudioProcessor* audioProcessor; + IEditController* editController; + IMidiMapping* midiMapping; + + Timer* timer; + + HostProcessData processData; + ParameterChanges processParamChanges; + ParameterChanges outputParamChanges; + ParameterChangeTransfer transferParamChanges; + ParameterChangeTransfer outputParamTransfer; + ProcessContext processContext; + EventList eventList; + + typedef std::map CachedParameterInfoMap; + typedef std::map UnitInfoMap; + typedef std::vector ClumpGroupVector; + + UnitInfoMap unitInfos; + ClumpGroupVector clumpGroups; + CachedParameterInfoMap cachedParameterInfos; + FLock parameterCacheChanging; + + NoteInstanceID noteCounter; + double sampleRate; + ParamID bypassParamID; + + AUPreset* presets; + int32 numPresets; + ParamID factoryProgramChangedID; + + bool isInstrument; + bool isBypassed; + + AUParameterListenerRef paramListenerRef; + static const int32 kMaxProgramChangeParameters = 16; + ParamID programChangeParameters[kMaxProgramChangeParameters]; // for each midi channel + + int32 midiOutCount; // currently only 0 or 1 supported + MIDIOutputCallbackHelper mCallbackHelper; + EventList outputEvents; +private: + void buildUnitInfos (IUnitInfo* unitInfoController, UnitInfoMap& units) const; +}; + +//------------------------------------------------------------------------ +class AutoreleasePool +{ +public: + AutoreleasePool () { ap = [[NSAutoreleasePool alloc] init]; } + ~AutoreleasePool () { [ap drain]; } +//------------------------------------------------------------------------ +protected: + NSAutoreleasePool* ap; +}; + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg + +/// \endcond diff --git a/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/auwrapper.mm b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/auwrapper.mm new file mode 100644 index 000000000..aa5c3421c --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/auwrapper.mm @@ -0,0 +1,2582 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/auwrapper/auwrapper.mm +// Created by : Steinberg, 12/2007 +// Description : VST 3 -> AU Wrapper +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +/// \cond ignore + +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + +/* + +Things to do : + - Parameter Mapping could be better (indexed stuff), but needs work if we want to have this + - Speaker Arrangement -> Channel Layout (partially done, just doesn't work always in Logic 8) + - dynamic Bus management + +*/ + +#include "auwrapper.h" +#include "aucocoaview.h" +#include "NSDataIBStream.h" +#include "pluginterfaces/gui/iplugview.h" +#include "pluginterfaces/base/ustring.h" +#include "pluginterfaces/vst/ivstmidicontrollers.h" +#include "pluginterfaces/vst/vsttypes.h" +#include "pluginterfaces/vst/vstpresetkeys.h" +#include "public.sdk/source/vst/hosting/eventlist.h" +#include "public.sdk/source/vst/hosting/processdata.h" +#include "public.sdk/source/vst/hosting/parameterchanges.h" +#include "public.sdk/source/vst/hosting/hostclasses.h" +#include "public.sdk/source/vst/vsteditcontroller.h" +#include "base/source/fdynlib.h" +#include "base/source/fstring.h" +#include +#if !__LP64__ +#include +#endif +#include +#include +#include +#if !CA_USE_AUDIO_PLUGIN_ONLY +#include "CAXException.h" +#endif + +#define SMTG_MAKE_STRING_PRIVATE_DONT_USE(x) #x +#define SMTG_MAKE_STRING(x) SMTG_MAKE_STRING_PRIVATE_DONT_USE (x) + +typedef bool (*bundleEntryPtr) (CFBundleRef); +typedef bool (*bundleExitPtr) (void); + +#include + +// we ignore for the moment that the NSAddImage functions are deprecated +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + +static bool CopyProcessPath (Steinberg::String& name) +{ + Dl_info info; + if (dladdr ((const void*)CopyProcessPath, &info)) + { + if (info.dli_fname) + { + name.assign (info.dli_fname); +#ifdef UNICODE + name.toWideString (); +#endif + return true; + } + } + return false; +} + +namespace Steinberg { + +//------------------------------------------------------------------------ +DEF_CLASS_IID (IPluginBase) +DEF_CLASS_IID (IBStream) +DEF_CLASS_IID (IPlugView) +DEF_CLASS_IID (IPluginFactory2) + +//------------------------------------------------------------------------ +class VST3DynLibrary : public FDynLibrary +{ +public: + VST3DynLibrary () : bundleEntryCalled (false) { gInstance = this; } + + ~VST3DynLibrary () + { + if (isLoaded ()) + { + if (bundleEntryCalled) + { + if (bundleExitPtr bundleExit = (bundleExitPtr)getProcAddress ("bundleExit")) + bundleExit (); + } + +#if defined(MAC_OS_X_VERSION_10_11) + // workaround, because CFBundleCreate returns refcount == 2. + if (CFBundleIsExecutableLoaded ((CFBundleRef)instance)) + { + CFBundleUnloadExecutable ((CFBundleRef)instance); + CFRelease ((CFBundleRef)instance); + } +#else + CFRelease ((CFBundleRef)instance); +#endif + instance = 0; + isloaded = false; + } + gInstance = 0; + } + + bool init (const tchar* path) + { + if (isLoaded ()) + return true; + + isBundle = false; + + Steinberg::String name (path); + + if (name.getChar16 (0) != STR ('/')) // no absoltue path + { + Steinberg::String p; + if (CopyProcessPath (p)) + { + Steinberg::int32 index = p.findLast (STR ('/')); + p.remove (index + 1); + name = p + name; + } + } + + CFStringRef fsString = (CFStringRef)name.toCFStringRef (); + CFURLRef url = CFURLCreateWithFileSystemPath (NULL, fsString, kCFURLPOSIXPathStyle, true); + if (url) + { + CFBundleRef bundle = CFBundleCreate (NULL, url); + if (bundle) + { + bundleEntryPtr bundleEntry = (bundleEntryPtr)CFBundleGetFunctionPointerForName ( + bundle, CFSTR ("bundleEntry")); + if (bundleEntry) + bundleEntryCalled = bundleEntry (bundle); + + if (bundleEntryCalled) + { + isBundle = true; + isloaded = true; + instance = (void*)bundle; + } + else + CFRelease (bundle); + } + CFRelease (url); + } + CFRelease (fsString); + + return isLoaded (); + } + + static VST3DynLibrary* gInstance; + +protected: + bool bundleEntryCalled; +}; + +VST3DynLibrary* VST3DynLibrary::gInstance = 0; + +namespace Vst { + +const ParamID kNoParamId = std::numeric_limits::max (); + +//------------------------------------------------------------------------ +DEF_CLASS_IID (IEventList) +DEF_CLASS_IID (IHostApplication) +DEF_CLASS_IID (IParameterChanges) +DEF_CLASS_IID (IParamValueQueue) +DEF_CLASS_IID (IMessage) +DEF_CLASS_IID (IAttributeList) +DEF_CLASS_IID (IComponent) +DEF_CLASS_IID (IComponentHandler) +DEF_CLASS_IID (IAudioProcessor) +DEF_CLASS_IID (IEditController) +DEF_CLASS_IID (IMidiMapping) +DEF_CLASS_IID (IUnitInfo) +DEF_CLASS_IID (IConnectionPoint) +DEF_CLASS_IID (IVst3ToVst2Wrapper) +DEF_CLASS_IID (IVst3ToAUWrapper) + +//-------------------------------------------------------------------------------------------- +class SpeakerArrangementBase +{ +public: + SpeakerArrangementBase () + { + channelLayout.mChannelBitmap = 0; + channelLayout.mChannelLayoutTag = 0; + channelLayout.mNumberChannelDescriptions = 0; + } + + OSStatus setNumChannels (int32 numChannels) + { + switch (numChannels) + { + case 1: channelLayout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono; break; + case 2: channelLayout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo; break; + case 6: channelLayout.mChannelLayoutTag = kAudioChannelLayoutTag_AudioUnit_5_1; break; + default: return kAudioUnitErr_InvalidProperty; + } + return noErr; + } + +protected: + AudioChannelLayout channelLayout; +}; + +//-------------------------------------------------------------------------------------------- +class MultiChannelOutputElement : public AUOutputElement, public SpeakerArrangementBase +{ +public: + MultiChannelOutputElement (AUBase* audioUnit) : AUOutputElement (audioUnit) {} + + UInt32 GetChannelLayoutTags (AudioChannelLayoutTag* outLayoutTagsPtr) + { + if (AudioChannelLayoutTag_GetNumberOfChannels (channelLayout.mChannelLayoutTag) <= 2) + return 0; + + if (outLayoutTagsPtr) + *outLayoutTagsPtr = channelLayout.mChannelLayoutTag; + return 1; + } + + UInt32 GetAudioChannelLayout (AudioChannelLayout* outMapPtr, Boolean& outWritable) + { + if (AudioChannelLayoutTag_GetNumberOfChannels (channelLayout.mChannelLayoutTag) <= 2) + return 0; + + if (outMapPtr) + *outMapPtr = channelLayout; + outWritable = false; + return sizeof (AudioChannelLayout); + } + + OSStatus SetAudioChannelLayout (const AudioChannelLayout& layout) + { + return kAudioUnitErr_InvalidProperty; + } + + OSStatus SetStreamFormat (const CAStreamBasicDescription& desc) + { + OSStatus err = setNumChannels (desc.NumberChannels ()); + if (err != noErr) + return err; + return AUOutputElement::SetStreamFormat (desc); + } +}; + +//-------------------------------------------------------------------------------------------- +class MultiChannelInputElement : public AUInputElement, public SpeakerArrangementBase +{ +public: + MultiChannelInputElement (AUBase* audioUnit) : AUInputElement (audioUnit) {} + + UInt32 GetChannelLayoutTags (AudioChannelLayoutTag* outLayoutTagsPtr) + { + if (AudioChannelLayoutTag_GetNumberOfChannels (channelLayout.mChannelLayoutTag) <= 2) + return 0; + + if (outLayoutTagsPtr) + *outLayoutTagsPtr = channelLayout.mChannelLayoutTag; + return 1; + } + + UInt32 GetAudioChannelLayout (AudioChannelLayout* outMapPtr, Boolean& outWritable) + { + if (AudioChannelLayoutTag_GetNumberOfChannels (channelLayout.mChannelLayoutTag) <= 2) + return 0; + + if (outMapPtr) + memcpy (outMapPtr, &channelLayout, sizeof (AudioChannelLayout)); + outWritable = false; + return sizeof (AudioChannelLayout); + } + + OSStatus SetAudioChannelLayout (const AudioChannelLayout& layout) + { + return kAudioUnitErr_InvalidProperty; + } + + OSStatus SetStreamFormat (const CAStreamBasicDescription& desc) + { + OSStatus err = setNumChannels (desc.NumberChannels ()); + if (err != noErr) + return err; + return AUInputElement::SetStreamFormat (desc); + } +}; + +//------------------------------------------------------------------------ +static CFStringRef createCFStringFromString128 (const String128& string) +{ + UString128 str (string); + return CFStringCreateWithCharacters (0, (const UniChar*)string, str.getLength ()); +} + +//------------------------------------------------------------------------ +static void createString128FromCFString (CFStringRef inString, String128& outString) +{ + CFStringGetCharacters (inString, + CFRangeMake (0, std::max (128, CFStringGetLength (inString))), + (UniChar*)outString); +} + +//------------------------------------------------------------------------ +static CFBundleRef GetBundleFromExecutable (const char* filepath) +{ + AutoreleasePool ap; + NSString* execStr = [NSString stringWithCString:filepath encoding:NSUTF8StringEncoding]; + NSString* macOSStr = [execStr stringByDeletingLastPathComponent]; + NSString* contentsStr = [macOSStr stringByDeletingLastPathComponent]; + NSString* bundleStr = [contentsStr stringByDeletingLastPathComponent]; + return CFBundleCreate (0, (CFURLRef)[NSURL fileURLWithPath:bundleStr isDirectory:YES]); +} + +//------------------------------------------------------------------------ +static CFBundleRef GetCurrentBundle () +{ + Dl_info info; + if (dladdr ((const void*)GetCurrentBundle, &info)) + { + if (info.dli_fname) + { + return GetBundleFromExecutable (info.dli_fname); + } + } + return 0; +} + +//------------------------------------------------------------------------ +CFBundleRef AUWrapper::gBundleRef = 0; +static CFIndex gBundleRefCount = 0; +static AUChannelInfo* channelInfos = 0; + +//------------------------------------------------------------------------ +static void initBundleRef () +{ + if (AUWrapper::gBundleRef == 0) + { + AUWrapper::gBundleRef = GetCurrentBundle (); + gBundleRefCount = CFGetRetainCount (AUWrapper::gBundleRef); + } + else + CFRetain (AUWrapper::gBundleRef); +} + +//------------------------------------------------------------------------ +static void releaseBundleRef () +{ + CFIndex currentCount = CFGetRetainCount (AUWrapper::gBundleRef); + CFRelease (AUWrapper::gBundleRef); + if (currentCount == gBundleRefCount) + AUWrapper::gBundleRef = 0; +} + +//------------------------------------------------------------------------ +class AUHostApplication : public HostApplication, public IVst3ToAUWrapper +{ +public: + virtual tresult PLUGIN_API getName (String128 name) SMTG_OVERRIDE + { + String str ("VST3-AU Wrapper"); + str.copyTo (name, 0, 127); + return kResultTrue; + } + + DEFINE_INTERFACES + DEF_INTERFACE (Vst::IVst3ToAUWrapper) + END_DEFINE_INTERFACES (HostApplication) + REFCOUNT_METHODS (HostApplication) +}; + +static AUHostApplication gHostApp; + +//------------------------------------------------------------------------ +static void paramChangedListenerProc (void* inRefCon, void* inObject, + const AudioUnitParameter* inParameter, + AudioUnitParameterValue inValue) +{ + AUWrapper* auWrapper = (AUWrapper*)inRefCon; + if (inParameter && inObject != inRefCon) + auWrapper->setControllerParameter (inParameter->mParameterID, inValue); +} + +//------------------------------------------------------------------------ +IMPLEMENT_FUNKNOWN_METHODS (AUWrapper, IComponentHandler, IComponentHandler::iid) +//------------------------------------------------------------------------ +AUWrapper::AUWrapper (ComponentInstanceRecord* ci) +: AUWRAPPER_BASE_CLASS (ci, 0, 0) +, audioProcessor (0) +, editController (0) +, midiMapping (0) +, timer (0) +, noteCounter (0) +, sampleRate (44100) +, bypassParamID (-1) +, presets (0) +, numPresets (0) +, factoryProgramChangedID (-1) +, isInstrument (false) +, isBypassed (false) +, paramListenerRef (0) +, midiOutCount (0) +{ + FUNKNOWN_CTOR + AutoreleasePool ap; + initBundleRef (); + + for (int32 i = 0; i < kMaxProgramChangeParameters; i++) + programChangeParameters[i] = kNoParamId; + + processData.processContext = &processContext; + processData.inputParameterChanges = &processParamChanges; + processData.outputParameterChanges = &outputParamChanges; + eventList.setMaxSize (128); + outputEvents.setMaxSize (128); + processData.inputEvents = &eventList; + processData.outputEvents = &outputEvents; + + loadVST3Module (); + + FUnknownPtr factory (owned (getFactory ())); + if (factory) + { + // find first audio processor class + for (int32 i = 0; i < factory->countClasses (); i++) + { + PClassInfo2 ci; + if (factory->getClassInfo2 (i, &ci) == kResultTrue) + { + if (strcmp (ci.category, kVstAudioEffectClass) == 0) + { + if (factory->createInstance (ci.cid, IAudioProcessor::iid, + (void**)&audioProcessor) == kResultTrue) + { + ConstString plugCategory (ci.subCategories); + if (plugCategory.findFirst ("Instrument", -1, + ConstString::kCaseInsensitive) >= 0) + isInstrument = true; + break; + } + } + } + } + } + if (audioProcessor) + { + if (FUnknownPtr (audioProcessor)->initialize ((HostApplication*)&gHostApp) != + kResultTrue) + return; + + if (audioProcessor->queryInterface (IEditController::iid, (void**)&editController) != + kResultTrue) + { + FUnknownPtr component (audioProcessor); + if (component) + { + TUID ccid; + if (component->getControllerClassId (ccid) == kResultTrue) + { + if (factory->createInstance (ccid, IEditController::iid, + (void**)&editController) == kResultTrue) + { + if (editController->initialize ((HostApplication*)&gHostApp) != kResultTrue) + { + editController->release (); + editController = 0; + return; + } + + FUnknownPtr controllerConnectionPoint (editController); + FUnknownPtr processorConnectionPoint (audioProcessor); + if (controllerConnectionPoint && processorConnectionPoint) + { + controllerConnectionPoint->connect (processorConnectionPoint); + processorConnectionPoint->connect (controllerConnectionPoint); + } + + NSMutableData* processorData = [[[NSMutableData alloc] init] autorelease]; + NSMutableDataIBStream stream (processorData); + if (FUnknownPtr (audioProcessor)->getState (&stream) == + kResultTrue) + { + stream.seek (0, IBStream::kIBSeekSet); + editController->setComponentState (&stream); + } + } + } + } + } + if (editController) + { + editController->setComponentHandler (this); + // initialize buses + FUnknownPtr component (audioProcessor); + int32 inputBusCount = component->getBusCount (kAudio, kInput); + int32 outputBusCount = component->getBusCount (kAudio, kOutput); + + CreateElements (); + Inputs ().SetNumberOfElements (inputBusCount); + Outputs ().SetNumberOfElements (outputBusCount); + + SpeakerArrangement sa; + for (int32 inputNo = 0; inputNo < inputBusCount; inputNo++) + { + MultiChannelInputElement* element = + dynamic_cast (Inputs ().GetIOElement (inputNo)); + if (element == 0) + continue; + if (audioProcessor->getBusArrangement (kInput, inputNo, sa) == kResultTrue) + { + CAStreamBasicDescription streamDesc (element->GetStreamFormat ()); + streamDesc.ChangeNumberChannels (SpeakerArr::getChannelCount (sa), false); + element->SetStreamFormat (streamDesc); + } + BusInfo info = {0}; + if (component->getBusInfo (kAudio, kInput, inputNo, info) == kResultTrue) + { + String busName (info.name); + CFStringRef busNameString = (CFStringRef)busName.toCFStringRef (); + element->SetName (busNameString); + CFRelease (busNameString); + } + component->activateBus (kAudio, kInput, inputNo, true); + } + for (int32 outputNo = 0; outputNo < outputBusCount; outputNo++) + { + MultiChannelOutputElement* element = + dynamic_cast (Outputs ().GetIOElement (outputNo)); + if (element == 0) + continue; + if (audioProcessor->getBusArrangement (kOutput, outputNo, sa) == kResultTrue) + { + CAStreamBasicDescription streamDesc (element->GetStreamFormat ()); + streamDesc.ChangeNumberChannels (SpeakerArr::getChannelCount (sa), false); + element->SetStreamFormat (streamDesc); + } + BusInfo info = {0}; + if (component->getBusInfo (kAudio, kOutput, outputNo, info) == kResultTrue) + { + String busName (info.name); + CFStringRef busNameString = (CFStringRef)busName.toCFStringRef (); + element->SetName (busNameString); + CFRelease (busNameString); + } + component->activateBus (kAudio, kOutput, outputNo, true); + } + processData.prepare (*component); + + // initialize parameters + syncParameterValues (); + cacheParameterValues (); + + midiOutCount = component->getBusCount (kEvent, kOutput); + + editController->queryInterface (IMidiMapping::iid, (void**)&midiMapping); + + transferParamChanges.setMaxParameters (500); + outputParamTransfer.setMaxParameters (500); + + timer = Timer::create (this, 20); + } + } +} + +//------------------------------------------------------------------------ +AUWrapper::~AUWrapper () +{ + AutoreleasePool ap; + if (timer) + timer->release (); + if (paramListenerRef) + AUListenerDispose (paramListenerRef); + if (audioProcessor) + { + FUnknownPtr combined (audioProcessor); + if (!combined) + { + FUnknownPtr controllerConnectionPoint (editController); + FUnknownPtr processorConnectionPoint (audioProcessor); + if (controllerConnectionPoint && processorConnectionPoint) + { + controllerConnectionPoint->disconnect (processorConnectionPoint); + processorConnectionPoint->disconnect (controllerConnectionPoint); + } + } + } + if (midiMapping) + { + midiMapping->release (); + midiMapping = 0; + } + if (editController) + { + editController->setComponentHandler (0); + uint32 refCount = editController->addRef (); + if (refCount == 2) + editController->terminate (); + + editController->release (); + editController->release (); + editController = 0; + } + + clearParameterValueCache (); + + if (audioProcessor) + { + FUnknownPtr (audioProcessor)->terminate (); + audioProcessor->release (); + audioProcessor = 0; + } + unloadVST3Module (); + releaseBundleRef (); + + if (presets) + delete presets; + + FUNKNOWN_DTOR +} + +//------------------------------------------------------------------------ +void AUWrapper::loadVST3Module () +{ + if (VST3DynLibrary::gInstance == 0) + { + if (gBundleRef) + { + String pluginPath; + CFStringRef pluginNameStr = (CFStringRef)CFBundleGetValueForInfoDictionaryKey ( + gBundleRef, CFSTR ("VST3 Plug-in")); + if (!pluginNameStr) + { + NSURL* pluginUrl = (NSURL*)CFBundleCopyResourceURL (gBundleRef, CFSTR ("plugin"), + CFSTR ("vst3"), NULL); + if (!pluginUrl) + { + NSLog ( + @"VST3 Plug-in not defined in Info Dictionary and not included in bundle"); + return; + } + pluginPath.fromCFStringRef ((CFStringRef)[pluginUrl path]); + [pluginUrl release]; + } + else + { + pluginPath.fromCFStringRef (pluginNameStr); + if (!pluginPath.getChar (0) != STR ('/')) + { + FSRef fsRef; + if (FSFindFolder (kLocalDomain, kAudioPlugInsFolderType, false, &fsRef) == + noErr) + { + NSURL* url = (NSURL*)CFURLCreateFromFSRef (0, &fsRef); + if (url) + { + String basePath ([[url path] UTF8String]); + basePath.toWideString (kCP_Utf8); + pluginPath.insertAt (0, STR ("/VST3/")); + pluginPath.insertAt (0, basePath); + [url release]; + } + } + } + } + + new VST3DynLibrary (); + if (pluginPath.isEmpty () || !VST3DynLibrary::gInstance->init (pluginPath)) + { + VST3DynLibrary::gInstance->release (); + return; + } + } + } + else + VST3DynLibrary::gInstance->addRef (); +} + +//------------------------------------------------------------------------ +void AUWrapper::unloadVST3Module () +{ + if (VST3DynLibrary::gInstance) + { + VST3DynLibrary::gInstance->release (); + } +} + +//------------------------------------------------------------------------ +IPluginFactory* AUWrapper::getFactory () +{ + if (VST3DynLibrary::gInstance) + { + GetFactoryProc getPlugFactory = + (GetFactoryProc)VST3DynLibrary::gInstance->getProcAddress ("GetPluginFactory"); + if (getPlugFactory) + return getPlugFactory (); + } + return 0; +} + +//------------------------------------------------------------------------ +// ComponentBase +ComponentResult AUWrapper::Version () +{ + AutoreleasePool ap; + NSString* versionString = + (NSString*)CFBundleGetValueForInfoDictionaryKey (gBundleRef, CFSTR ("AudioUnit Version")); + if (versionString) + { + int version = 0; + if (sscanf ([versionString UTF8String], "%x", &version) == 1) + return version; + } + return 0xFFFFFFFF; +} + +//------------------------------------------------------------------------ +void AUWrapper::PostConstructor () +{ + AUWRAPPER_BASE_CLASS::PostConstructor (); +} + +//------------------------------------------------------------------------ +static SpeakerArrangement numChannelsToSpeakerArrangement (UInt32 numChannels) +{ + switch (numChannels) + { + case 1: return SpeakerArr::kMono; + case 2: return SpeakerArr::kStereo; + case 6: return SpeakerArr::k51; + } + return 0; +} + +//------------------------------------------------------------------------ +ComponentResult AUWrapper::Initialize () +{ + if (audioProcessor && editController) + { + // match speaker arrangement with AU stream format + FUnknownPtr component (audioProcessor); + int32 inputBusCount = component->getBusCount (kAudio, kInput); + int32 outputBusCount = component->getBusCount (kAudio, kOutput); + + SupportedNumChannels (0); // initialize channelInfos + if (channelInfos) + { + int32 maxBusCount = std::max (inputBusCount, outputBusCount); + for (int32 i = 0; i < maxBusCount; i++) + { + int32 inChannelCount = 0; + int32 outChannelCount = 0; + if (inputBusCount > i) + inChannelCount = + Inputs ().GetIOElement (i)->GetStreamFormat ().NumberChannels (); + if (outputBusCount > i) + outChannelCount = + Outputs ().GetIOElement (i)->GetStreamFormat ().NumberChannels (); + if (!validateChannelPair (inChannelCount, outChannelCount, channelInfos, + SupportedNumChannels (0))) + return kAudioUnitErr_FailedInitialization; + } + } + + SpeakerArrangement inputs[inputBusCount]; + SpeakerArrangement outputs[outputBusCount]; + for (int32 element = 0; element < inputBusCount; element++) + { + const CAStreamBasicDescription desc = + Inputs ().GetIOElement (element)->GetStreamFormat (); + inputs[element] = numChannelsToSpeakerArrangement (desc.NumberChannels ()); + } + for (int32 element = 0; element < outputBusCount; element++) + { + const CAStreamBasicDescription desc = + Outputs ().GetIOElement (element)->GetStreamFormat (); + outputs[element] = numChannelsToSpeakerArrangement (desc.NumberChannels ()); + } + if (audioProcessor->setBusArrangements (inputs, inputBusCount, outputs, outputBusCount) != + kResultTrue) + { + return kAudioUnitErr_FailedInitialization; + } + + // After set Bus Arrangement, the channelbuffers may need to be reallocated -> hence the + // second prepare! + processData.prepare (*component); + + ProcessSetup ps; + ps.sampleRate = getSampleRate (); + ps.maxSamplesPerBlock = GetMaxFramesPerSlice (); + ps.symbolicSampleSize = kSample32; + ps.processMode = kRealtime; + audioProcessor->setupProcessing (ps); + + component->setActive (true); + + audioProcessor->setProcessing (true); + + if (paramListenerRef == 0) + { + OSStatus status = + AUListenerCreate (paramChangedListenerProc, this, CFRunLoopGetCurrent (), + kCFRunLoopDefaultMode, 0.2, ¶mListenerRef); + ASSERT (status == noErr) + if (paramListenerRef) + { + AudioUnitParameter sPar; + sPar.mAudioUnit = GetComponentInstance (); + sPar.mParameterID = 0; + sPar.mElement = 0; + sPar.mScope = kAudioUnitScope_Global; + + std::vector programParameters; + + // for each VST3 parameter + for (int32 i = 0; i < editController->getParameterCount (); i++) + { + ParameterInfo pi = {0}; + editController->getParameterInfo (i, pi); + + // do not register bypass + if ((pi.flags & ParameterInfo::kIsBypass) != 0) + { + continue; + } + + sPar.mParameterID = pi.id; + status = AUListenerAddParameter (paramListenerRef, 0, &sPar); + ASSERT (status == noErr) + + if ((pi.flags & ParameterInfo::kIsProgramChange) != 0) + { + programParameters.push_back (pi); + } + } + + // assign programChanges + for (int32 midiChannel = 0; midiChannel < kMaxProgramChangeParameters; + midiChannel++) + { + programChangeParameters[midiChannel] = kNoParamId; + UnitID unitId; + ProgramListID programListId; + if (getProgramListAndUnit (midiChannel, unitId, programListId)) + { + for (int32 i = 0; i < (int32)programParameters.size (); i++) + { + const ParameterInfo& paramInfo = programParameters.at (i); + if (paramInfo.unitId == unitId) + { + programChangeParameters[midiChannel] = paramInfo.id; + break; + } + } + } + } + + IUnitInfo* unitInfoController = NULL; + // let's see if there's a preset list (take the first one) + if (editController->queryInterface (IUnitInfo::iid, (void**)&unitInfoController) == + kResultTrue && + unitInfoController) + { + ProgramListInfo programListInfo; + if (unitInfoController->getProgramListInfo (0, programListInfo) == kResultTrue) + { + factoryProgramChangedID = -1; + numPresets = programListInfo.programCount; + if (programListInfo.programCount > 0) + { + UnitID unitId = -1; + + // find the unit supporting this ProgramList + IUnitInfo* unitInfo = NULL; + if (editController->queryInterface (IUnitInfo::iid, + (void**)&unitInfo) == kResultTrue && + unitInfo) + { + int32 unitCount = unitInfo->getUnitCount (); + for (int32 i = 0; i < unitCount; i++) + { + UnitInfo unitInfoStruct = {0}; + if (unitInfo->getUnitInfo (i, unitInfoStruct) == kResultTrue) + { + if (programListInfo.id == unitInfoStruct.programListId) + { + unitId = unitInfoStruct.id; + break; + } + } + } + + unitInfo->release (); + } + if (unitId != -1) + { + // find the associated ProgramChange parameter ID + for (int32 i = 0; i < (int32)programParameters.size (); i++) + { + const ParameterInfo& paramInfo = programParameters.at (i); + if (paramInfo.unitId == unitId) + { + factoryProgramChangedID = paramInfo.id; + break; + } + } + if (factoryProgramChangedID != -1) + { + presets = new AUPreset[programListInfo.programCount]; + for (int32 i = 0; i < programListInfo.programCount; i++) + { + String128 name; + unitInfoController->getProgramName (programListInfo.id, i, + name); + presets[i].presetNumber = i; + presets[i].presetName = createCFStringFromString128 (name); + } + } + } + } + } + unitInfoController->release (); + } + } + } + + return AUWRAPPER_BASE_CLASS::Initialize (); + } + return -1; +} + +//------------------------------------------------------------------------ +void AUWrapper::Cleanup () +{ + if (audioProcessor) + { + audioProcessor->setProcessing (false); + + FUnknownPtr component (audioProcessor); + component->setActive (false); + } +} + +//------------------------------------------------------------------------ +// AUBase +//-------------------------------------------------------------------------------------------- +AUElement* AUWrapper::CreateElement (AudioUnitScope scope, AudioUnitElement element) +{ + switch (scope) + { + case kAudioUnitScope_Output: return new MultiChannelOutputElement (this); + case kAudioUnitScope_Input: return new MultiChannelInputElement (this); + } + + return AUWRAPPER_BASE_CLASS::CreateElement (scope, element); +} + +//------------------------------------------------------------------------ +UInt32 AUWrapper::SupportedNumChannels (const AUChannelInfo** outInfo) +{ + static UInt32 numChannelInfos = 0; + static bool once = true; + if (once && gBundleRef) + { + once = false; + char buffer[128]; + + CFStringRef processName = 0; + ProcessSerialNumber psn; + OSErr err = GetCurrentProcess (&psn); + ASSERT (err == noErr); + OSStatus stat = CopyProcessName (&psn, &processName); + ASSERT (stat == noErr); + CFStringGetCString (processName, buffer, sizeof (buffer), kCFStringEncodingUTF8); + CFRelease (processName); + strncat (buffer, " SupportedNumChannels", sizeof (buffer) - strlen (buffer) - 1); + CFStringRef dictName = + CFStringCreateWithCString (kCFAllocatorDefault, buffer, kCFStringEncodingUTF8); + + NSArray* supportedNumChannelsArray = + (NSArray*)CFBundleGetValueForInfoDictionaryKey (gBundleRef, dictName); + if (!supportedNumChannelsArray) + supportedNumChannelsArray = (NSArray*)CFBundleGetValueForInfoDictionaryKey ( + gBundleRef, CFSTR ("AudioUnit SupportedNumChannels")); + if (supportedNumChannelsArray) + { + numChannelInfos = [supportedNumChannelsArray count]; + if (numChannelInfos > 0) + { + channelInfos = new AUChannelInfo[numChannelInfos]; + int i = 0; + for (NSDictionary* dict in supportedNumChannelsArray) + { + NSInteger inputs = [[dict objectForKey:@"Inputs"] integerValue]; + NSInteger outputs = [[dict objectForKey:@"Outputs"] integerValue]; + channelInfos[i].inChannels = inputs; + channelInfos[i++].outChannels = outputs; + } + } + } + CFRelease (dictName); + } + if (outInfo) + *outInfo = channelInfos; + return numChannelInfos; +} + +//------------------------------------------------------------------------ +bool AUWrapper::StreamFormatWritable (AudioUnitScope scope, AudioUnitElement element) +{ + return IsInitialized () ? false : true; +} + +//------------------------------------------------------------------------ +ComponentResult AUWrapper::ChangeStreamFormat (AudioUnitScope inScope, AudioUnitElement inElement, + const CAStreamBasicDescription& inPrevFormat, + const CAStreamBasicDescription& inNewFormat) +{ + if (inPrevFormat.NumberChannels () == inNewFormat.NumberChannels ()) + { + // sample rate change + ComponentResult res = AUWRAPPER_BASE_CLASS::ChangeStreamFormat (inScope, inElement, + inPrevFormat, inNewFormat); + if (res == noErr) + sampleRate = inNewFormat.mSampleRate; + return res; + } + else if (inPrevFormat.mSampleRate != inNewFormat.mSampleRate) + sampleRate = inNewFormat.mSampleRate; + else if (IsInitialized () || SupportedNumChannels (0) == 0) + return kAudioUnitErr_Initialized; + + AUIOElement* element; + + switch (inScope) + { + case kAudioUnitScope_Input: element = Inputs ().GetIOElement (inElement); break; + case kAudioUnitScope_Output: element = Outputs ().GetIOElement (inElement); break; + case kAudioUnitScope_Global: element = Outputs ().GetIOElement (0); break; + default: COMPONENT_THROW (kAudioUnitErr_InvalidScope); + } + OSStatus err = element->SetStreamFormat (inNewFormat); + if (err == noErr) + PropertyChanged (kAudioUnitProperty_StreamFormat, inScope, inElement); + return err; +} + +//------------------------------------------------------------------------ +ComponentResult AUWrapper::SetConnection (const AudioUnitConnection& inConnection) +{ + ComponentResult result = AUWRAPPER_BASE_CLASS::SetConnection (inConnection); + if (result == noErr) + { + int32 busIndex = inConnection.destInputNumber; + bool active = GetInput (busIndex)->IsActive (); + FUnknownPtr component (audioProcessor); + component->activateBus (kAudio, kInput, busIndex, active); + } + return result; +} + +static const UnitInfo kNoUnitInfo = {0}; +//------------------------------------------------------------------------ +void AUWrapper::buildUnitInfos (IUnitInfo* unitInfoController, UnitInfoMap& units) const +{ + units.clear (); + if (unitInfoController) + { + int32 numUnits = unitInfoController->getUnitCount (); + for (int32 i = 0; i < numUnits; i++) + { + UnitInfo ui; + if (unitInfoController->getUnitInfo (i, ui) == kResultTrue) + units[ui.id] = ui; + } + } +} + +//------------------------------------------------------------------------ +ComponentResult AUWrapper::GetParameterInfo (AudioUnitScope inScope, + AudioUnitParameterID inParameterID, + AudioUnitParameterInfo& outParameterInfo) +{ + if (inScope != kAudioUnitScope_Global) + return kAudioUnitErr_InvalidScope; + + FGuard guard (parameterCacheChanging); + CachedParameterInfoMap::const_iterator it = cachedParameterInfos.find (inParameterID); + if (it == cachedParameterInfos.end ()) + return kAudioUnitErr_InvalidParameter; + + memcpy (&outParameterInfo, &it->second, sizeof (AudioUnitParameterInfo)); + if (outParameterInfo.cfNameString) + CFRetain (outParameterInfo.cfNameString); + if (outParameterInfo.unitName) + CFRetain (outParameterInfo.unitName); + + return noErr; +} + +//------------------------------------------------------------------------ +ComponentResult AUWrapper::SetParameter (AudioUnitParameterID inID, AudioUnitScope inScope, + AudioUnitElement inElement, + AudioUnitParameterValue inValue, + UInt32 inBufferOffsetInFrames) +{ + if (inScope == kAudioUnitScope_Global) + { + if (inID == factoryProgramChangedID) + { + SetAFactoryPresetAsCurrent (presets[(int)((float)inValue * (float)numPresets)]); + PropertyChanged (kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0); + PropertyChanged (kAudioUnitProperty_CurrentPreset, kAudioUnitScope_Global, 0); + } + + transferParamChanges.addChange (inID, inValue, inBufferOffsetInFrames); + } + return AUWRAPPER_BASE_CLASS::SetParameter (inID, inScope, inElement, inValue, + inBufferOffsetInFrames); +} + +//------------------------------------------------------------------------ +void AUWrapper::setControllerParameter (ParamID pid, ParamValue value) +{ + if (editController && pid != factoryProgramChangedID) + editController->setParamNormalized (pid, value); +} + +//------------------------------------------------------------------------ +ComponentResult AUWrapper::SaveState (CFPropertyListRef* outData) +{ + ComponentResult result = AUWRAPPER_BASE_CLASS::SaveState (outData); + if (result != noErr) + return result; + + NSMutableDictionary* dict = (NSMutableDictionary*)*outData; + + AutoreleasePool ap; + NSMutableData* processorData = [[[NSMutableData alloc] init] autorelease]; + NSMutableData* controllerData = [[[NSMutableData alloc] init] autorelease]; + if (audioProcessor) + { + NSMutableDataIBStream stream (processorData); + if (FUnknownPtr (audioProcessor)->getState (&stream) != kResultTrue) + { + [processorData setLength:0]; + } + } + if (editController) + { + NSMutableDataIBStream stream (controllerData); + if (editController->getState (&stream) != kResultTrue) + { + [controllerData setLength:0]; + } + } + [dict setValue:processorData forKey:@"Processor State"]; + [dict setValue:controllerData forKey:@"Controller State"]; + *outData = dict; + return result; +} + +//------------------------------------------------------------------------ +ComponentResult AUWrapper::RestoreState (CFPropertyListRef inData) +{ + return restoreState (inData, false); +} + +//------------------------------------------------------------------------ +ComponentResult AUWrapper::restoreState (CFPropertyListRef inData, bool fromProject) +{ + AutoreleasePool ap; + + if (CFGetTypeID (inData) != CFDictionaryGetTypeID ()) + return kAudioUnitErr_InvalidPropertyValue; + + AudioComponentDescription desc = GetComponentDescription (); + + CFDictionaryRef dict = static_cast (inData); + + if (CFDictionaryContainsKey ( + dict, [NSString stringWithCString:kAUPresetPartKey encoding:NSASCIIStringEncoding])) + return kAudioUnitErr_InvalidPropertyValue; + +#define kCurrentSavedStateVersion 0 + + CFNumberRef cfnum = reinterpret_cast (CFDictionaryGetValue ( + dict, [NSString stringWithCString:kAUPresetVersionKey encoding:NSASCIIStringEncoding])); + if (cfnum == NULL) + return kAudioUnitErr_InvalidPropertyValue; + SInt32 value; + CFNumberGetValue (cfnum, kCFNumberSInt32Type, &value); + if (value != kCurrentSavedStateVersion) + return kAudioUnitErr_InvalidPropertyValue; + + cfnum = reinterpret_cast (CFDictionaryGetValue ( + dict, [NSString stringWithCString:kAUPresetSubtypeKey encoding:NSASCIIStringEncoding])); + if (cfnum == NULL) + return kAudioUnitErr_InvalidPropertyValue; + CFNumberGetValue (cfnum, kCFNumberSInt32Type, &value); + if (UInt32 (value) != desc.componentSubType) + return kAudioUnitErr_InvalidPropertyValue; + + cfnum = reinterpret_cast ( + CFDictionaryGetValue (dict, [NSString stringWithCString:kAUPresetManufacturerKey + encoding:NSASCIIStringEncoding])); + if (cfnum == NULL) + return kAudioUnitErr_InvalidPropertyValue; + CFNumberGetValue (cfnum, kCFNumberSInt32Type, &value); + if (UInt32 (value) != desc.componentManufacturer) + return kAudioUnitErr_InvalidPropertyValue; + + ComponentResult result = noErr; + + if (result == noErr && audioProcessor && editController && + CFGetTypeID (inData) == CFDictionaryGetTypeID ()) + { + // Note: the bypass state is not part of the AU state + bool wasBypassed = false; + if (bypassParamID != -1) + { + wasBypassed = editController->getParamNormalized (bypassParamID) >= 0.5 ? true : false; + } + + NSDictionary* dict = (NSDictionary*)inData; + + NSData* processorData = [dict valueForKey:@"Processor State"]; + int processLen = [processorData length]; + if (processLen == 0) + return kAudioUnitErr_InvalidPropertyValue; + + if (processorData) + { + NSDataIBStream stream (processorData); + if (fromProject) + stream.getAttributes ()->setString (Vst::PresetAttributes::kStateType, + String (Vst::StateType::kProject)); + FUnknownPtr (audioProcessor)->setState (&stream); + } + + NSData* controllerData = [dict valueForKey:@"Controller State"]; + if (controllerData) + { + NSDataIBStream processorStream (processorData); + editController->setComponentState (&processorStream); + + NSDataIBStream stream (controllerData); + if (fromProject) + stream.getAttributes ()->setString (Vst::PresetAttributes::kStateType, + String (Vst::StateType::kProject)); + editController->setState (&stream); + syncParameterValues (); + cacheParameterValues (); + } + if (bypassParamID != -1) + { + transferParamChanges.addChange (bypassParamID, wasBypassed ? 1 : 0, 0); + editController->setParamNormalized (bypassParamID, wasBypassed ? 1 : 0); + } + } + return result; +} + +//------------------------------------------------------------------------ +OSStatus AUWrapper::GetPresets (CFArrayRef* outData) const +{ + if (presets && numPresets > 0) + { + if (outData != 0) + { + CFMutableArrayRef presetsArray = CFArrayCreateMutable (NULL, numPresets, NULL); + for (int32 i = 0; i < numPresets; i++) + { + CFArrayAppendValue (presetsArray, &presets[i]); + } + *outData = (CFArrayRef)presetsArray; + } + return noErr; + } + return kAudioUnitErr_InvalidProperty; +} + +//------------------------------------------------------------------------ +OSStatus AUWrapper::NewFactoryPresetSet (const AUPreset& inNewFactoryPreset) +{ + if (numPresets > 0) + { + float normalizedValue = (float)inNewFactoryPreset.presetNumber / (float)numPresets; + transferParamChanges.addChange (factoryProgramChangedID, normalizedValue, 0); + editController->setParamNormalized (factoryProgramChangedID, normalizedValue); + + if (inNewFactoryPreset.presetNumber < numPresets) + SetAFactoryPresetAsCurrent (presets[inNewFactoryPreset.presetNumber]); + return noErr; + } + return kAudioUnitErr_InvalidProperty; +} + +//------------------------------------------------------------------------ +ComponentResult AUWrapper::Render (AudioUnitRenderActionFlags& ioActionFlags, + const AudioTimeStamp& inTimeStamp, UInt32 inNumberFrames) +{ + updateProcessContext (); + processContext.systemTime = inTimeStamp.mHostTime; + + processParamChanges.clearQueue (); + transferParamChanges.transferChangesTo (processParamChanges); + processData.numSamples = inNumberFrames; + + for (int32 i = 0; i < Inputs ().GetNumberOfElements (); i++) + { + AUInputElement* input = GetInput (i); + if (input->IsActive ()) + input->PullInput (ioActionFlags, inTimeStamp, i, inNumberFrames); + processData.inputs[i].numChannels = input->GetStreamFormat ().NumberChannels (); + for (int32 channel = 0; channel < input->GetStreamFormat ().NumberChannels (); channel++) + { + processData.inputs[i].channelBuffers32[channel] = + input->IsActive () ? (Sample32*)input->GetBufferList ().mBuffers[channel].mData : 0; + } + } + for (int32 i = 0; i < Outputs ().GetNumberOfElements (); i++) + { + AUOutputElement* output = GetOutput (i); + output->PrepareBuffer (inNumberFrames); + processData.outputs[i].numChannels = output->GetStreamFormat ().NumberChannels (); + for (int32 channel = 0; channel < output->GetStreamFormat ().NumberChannels (); channel++) + { + processData.outputs[i].channelBuffers32[channel] = + (Sample32*)output->GetBufferList ().mBuffers[channel].mData; + } + } + audioProcessor->process (processData); + + outputParamTransfer.transferChangesFrom (outputParamChanges); + outputParamChanges.clearQueue (); + eventList.clear (); + + ioActionFlags &= ~kAudioUnitRenderAction_OutputIsSilence; + + processOutputEvents (inTimeStamp); + + return noErr; +} + +const uint8 kNoteOff = 0x80; ///< note, off velocity +const uint8 kNoteOn = 0x90; ///< note, on velocity +const uint8 kPolyPressure = 0xA0; ///< note, pressure +const uint8 kController = 0xB0; ///< controller, value +const uint8 kProgramChangeStatus = 0xC0; ///< program change +const uint8 kAfterTouchStatus = 0xD0; ///< channel pressure +const uint8 kPitchBendStatus = 0xE0; ///< lsb, msb + +const float kMidiScaler = 1.f / 127.f; +static const uint8 kChannelMask = 0x0F; +static const uint8 kStatusMask = 0xF0; +static const uint32 kDataMask = 0x7F; + +//------------------------------------------------------------------------ +inline void AUWrapper::processOutputEvents (const AudioTimeStamp& inTimeStamp) +{ + if (midiOutCount > 0 && outputEvents.getEventCount () > 0) + { + int32 total = outputEvents.getEventCount (); + Event e = {0}; + + for (int32 i = 0; i < total; i++) + { + if (outputEvents.getEvent (i, e) != kResultOk) + break; + + //--- SYSEX ---------------- + if (e.type == Event::kDataEvent && e.data.type == DataEvent::kMidiSysEx) + { + } + else + { + switch (e.type) + { + case Event::kNoteOnEvent: + { + UInt8 status = (UInt8) (kNoteOn | (e.noteOn.channel & kChannelMask)); + UInt8 data1 = (UInt8) (e.noteOn.pitch & kDataMask); + UInt8 data2 = + (UInt8) ((int32) (e.noteOn.velocity * 127.f + 0.4999999f) & kDataMask); + UInt8 channel = e.noteOn.channel; + if (data2 == 0) // zero velocity => note off + status = (char)(kNoteOff | (e.noteOn.channel & kChannelMask)); + + mCallbackHelper.AddEvent (status, channel, data1, data2, e.sampleOffset); + } + break; + case Event::kNoteOffEvent: + { + UInt8 status = (UInt8) (kNoteOff | (e.noteOff.channel & kChannelMask)); + UInt8 data1 = e.noteOff.pitch; + UInt8 data2 = + (UInt8) ((int32) (e.noteOff.velocity * 127.f + 0.4999999f) & kDataMask); + UInt8 channel = e.noteOff.channel; + + mCallbackHelper.AddEvent (status, channel, data1, data2, e.sampleOffset); + } + break; + } + } + } + + outputEvents.clear (); + mCallbackHelper.FireAtTimeStamp (inTimeStamp); + } +} + +//-------------------------------------------------------------------------------------------- +ComponentResult AUWrapper::GetPropertyInfo (AudioUnitPropertyID inID, AudioUnitScope inScope, + AudioUnitElement inElement, UInt32& outDataSize, + Boolean& outWritable) +{ + switch (inID) + { + //-------------------------- + case kAudioUnitProperty_BypassEffect: + { + if (inScope == kAudioUnitScope_Global && bypassParamID != -1) + { + outWritable = true; + outDataSize = sizeof (UInt32); + return noErr; + } + return kAudioUnitErr_InvalidProperty; + } + //-------------------------- + case kAudioUnitProperty_ParameterClumpName: + { + if (bypassParamID != -1) + { + outWritable = false; + outDataSize = sizeof (AudioUnitParameterNameInfo); + return noErr; + } + break; + } + //-------------------------- + case kAudioUnitProperty_ParameterStringFromValue: + { + if (inScope == kAudioUnitScope_Global) + { + outDataSize = sizeof (AudioUnitParameterStringFromValue); + outWritable = false; + return noErr; + } + return kAudioUnitErr_InvalidProperty; + } + //-------------------------- + case kAudioUnitProperty_ParameterValueFromString: + { + if (inScope == kAudioUnitScope_Global) + { + outDataSize = sizeof (AudioUnitParameterValueFromString); + outWritable = false; + return noErr; + } + return kAudioUnitErr_InvalidProperty; + } + //-------------------------- + case kAudioUnitProperty_ElementName: + { + if (inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Output) + { + outDataSize = sizeof (CFStringRef); + return noErr; + } + break; + } + //-------------------------- + case kAudioUnitProperty_CocoaUI: + { + outWritable = false; + outDataSize = sizeof (AudioUnitCocoaViewInfo); + return noErr; + } + //-------------------------- + case kAudioUnitProperty_MIDIOutputCallbackInfo: + { + if (inScope == kAudioUnitScope_Global && midiOutCount > 0) + { + outDataSize = sizeof (CFArrayRef); + outWritable = false; + return noErr; + } + break; + } + //-------------------------- + case kAudioUnitProperty_MIDIOutputCallback: + { + if (inScope == kAudioUnitScope_Global && midiOutCount > 0) + { + outDataSize = sizeof (AUMIDIOutputCallbackStruct); + outWritable = true; + return noErr; + } + break; + } + //-------------------------- + case 64000: + { + if (editController) + { + outDataSize = sizeof (TPtrInt); + outWritable = false; + return noErr; + } + return kAudioUnitErr_InvalidProperty; + } + //-------------------------- + case 64001: + { + if (VST3DynLibrary::gInstance) + { + outDataSize = sizeof (TPtrInt); + outWritable = false; + return noErr; + } + return kAudioUnitErr_InvalidProperty; + } + } + return AUWRAPPER_BASE_CLASS::GetPropertyInfo (inID, inScope, inElement, outDataSize, + outWritable); +} + +//-------------------------------------------------------------------------------------------- +ComponentResult AUWrapper::SetProperty (AudioUnitPropertyID inID, AudioUnitScope inScope, + AudioUnitElement inElement, const void* inData, + UInt32 inDataSize) +{ + switch (inID) + { + //-------------------------- + case kAudioUnitProperty_BypassEffect: + { + if (inScope == kAudioUnitScope_Global && bypassParamID != -1) + { + bool tempNewSetting = *((UInt32*)inData) != 0; + transferParamChanges.addChange (bypassParamID, tempNewSetting ? 1 : 0, 0); + editController->setParamNormalized (bypassParamID, tempNewSetting ? 1 : 0); + isBypassed = tempNewSetting; + return noErr; + } + break; + } + //-------------------------- + case kAudioUnitProperty_MIDIOutputCallback: + { + if (inScope == kAudioUnitScope_Global && midiOutCount > 0) + { + if (inDataSize < sizeof (AUMIDIOutputCallbackStruct)) + return kAudioUnitErr_InvalidPropertyValue; + + AUMIDIOutputCallbackStruct* callbackStruct = (AUMIDIOutputCallbackStruct*)inData; + mCallbackHelper.SetCallbackInfo (callbackStruct->midiOutputCallback, + callbackStruct->userData); + return noErr; + } + break; + } + } + return AUWRAPPER_BASE_CLASS::SetProperty (inID, inScope, inElement, inData, inDataSize); +} + +//-------------------------------------------------------------------------------------------- +bool AUWrapper::CanScheduleParameters () const +{ + return false; +} + +//-------------------------------------------------------------------------------------------- +ComponentResult AUWrapper::GetProperty (AudioUnitPropertyID inID, AudioUnitScope inScope, + AudioUnitElement inElement, void* outData) +{ + switch (inID) + { + //-------------------------- + case kAudioUnitProperty_BypassEffect: + { + if (inScope == kAudioUnitScope_Global && bypassParamID != -1) + { + UInt32 bypass = editController->getParamNormalized (bypassParamID) >= 0.5 ? 1 : 0; + *((UInt32*)outData) = bypass; + return noErr; + } + break; + } + //-------------------------- + case kAudioUnitProperty_ParameterClumpName: + { + AudioUnitParameterNameInfo* parameterNameInfo = (AudioUnitParameterNameInfo*)outData; + int32 clumpId = parameterNameInfo->inID; + + if (clumpId < 1 || clumpId > clumpGroups.size ()) + return kAudioUnitErr_PropertyNotInUse; + + const String& clumpGroup = clumpGroups.at (clumpId - 1); + parameterNameInfo->outName = (CFStringRef)clumpGroup.toCFStringRef (); + + return noErr; + } + //-------------------------- + case kAudioUnitProperty_ParameterStringFromValue: + { + if (inScope == kAudioUnitScope_Global) + { + AudioUnitParameterStringFromValue* ps = (AudioUnitParameterStringFromValue*)outData; + String128 paramString; + if (editController->getParamStringByValue ( + ps->inParamID, + ps->inValue ? *ps->inValue : + editController->getParamNormalized (ps->inParamID), + paramString) == kResultTrue) + ps->outString = createCFStringFromString128 (paramString); + else + ps->outString = CFStringCreateWithCString (0, "", kCFStringEncodingUTF8); + return noErr; + } + return kAudioUnitErr_InvalidProperty; + } + //-------------------------- + case kAudioUnitProperty_ParameterValueFromString: + { + if (inScope == kAudioUnitScope_Global) + { + AudioUnitParameterValueFromString* ps = (AudioUnitParameterValueFromString*)outData; + String128 paramString; + createString128FromCFString (ps->inString, paramString); + ParamValue value; + if (editController->getParamValueByString (ps->inParamID, paramString, value) == + kResultTrue) + { + ps->outValue = value; + return noErr; + } + return -1; + } + return kAudioUnitErr_InvalidProperty; + } + //-------------------------- + case kAudioUnitProperty_ElementName: + { + if (inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Output) + { + FUnknownPtr component (audioProcessor); + BusInfo busInfo; + if (component->getBusInfo (kAudio, + inScope == kAudioUnitScope_Input ? kInput : kOutput, + inElement, busInfo) == kResultTrue) + { + outData = (void*)createCFStringFromString128 (busInfo.name); + return noErr; + } + } + return kAudioUnitErr_InvalidProperty; + } + //-------------------------- + case kAudioUnitProperty_CocoaUI: + { + AutoreleasePool ap; + + CFStringRef className = (CFStringRef)[[NSString alloc] + initWithCString:SMTG_MAKE_STRING (SMTG_AU_PLUGIN_NAMESPACE (SMTGAUPluginCocoaView)) + encoding:NSUTF8StringEncoding]; + const char* image = class_getImageName (objc_getClass ( + SMTG_MAKE_STRING (SMTG_AU_PLUGIN_NAMESPACE (SMTGAUPluginCocoaView)))); + CFBundleRef bundle = GetBundleFromExecutable (image); + + CFURLRef url = CFBundleCopyBundleURL (bundle); + CFRelease (bundle); + AudioUnitCocoaViewInfo cocoaInfo = {url, {className}}; + *((AudioUnitCocoaViewInfo*)outData) = cocoaInfo; + return noErr; + } + //-------------------------- + case kAudioUnitProperty_MIDIOutputCallbackInfo: + { + if (inScope == kAudioUnitScope_Global && midiOutCount > 0) + { + CFStringRef strs[1]; + strs[0] = CFSTR ("MIDI Callback"); + + CFArrayRef callbackArray = + CFArrayCreate (NULL, (const void**)strs, 1, &kCFTypeArrayCallBacks); + *(CFArrayRef*)outData = callbackArray; + + return noErr; + } + return kAudioUnitErr_InvalidProperty; + } + //-------------------------- + case 64000: + { + if (editController) + { + TPtrInt ptr = (TPtrInt)editController; + *((TPtrInt*)outData) = ptr; + return noErr; + } + else + *((TPtrInt*)outData) = 0; + return kAudioUnitErr_InvalidProperty; + } + //-------------------------- + case 64001: + { + if (VST3DynLibrary::gInstance) + { + TPtrInt ptr = (TPtrInt)VST3DynLibrary::gInstance; + *((TPtrInt*)outData) = ptr; + return noErr; + } + else + *((TPtrInt*)outData) = 0; + return kAudioUnitErr_InvalidProperty; + } + } + return AUWRAPPER_BASE_CLASS::GetProperty (inID, inScope, inElement, outData); +} + +//------------------------------------------------------------------------ +int AUWrapper::GetNumCustomUIComponents () +{ + if (editController) + { + static int numUIComponents = 0; + static bool once = true; + if (once) + { + AutoreleasePool ap; + once = false; + IPlugView* plugView = editController->createView (ViewType::kEditor); + if (plugView) + { + + if (plugView->isPlatformTypeSupported (kPlatformTypeHIView) == kResultTrue) + numUIComponents = 1; + plugView->release (); + } + } + return numUIComponents; + } + return 0; +} + +//------------------------------------------------------------------------ +void AUWrapper::GetUIComponentDescs (ComponentDescription* inDescArray) +{ +#if !__LP64__ + if (editController && GetNumCustomUIComponents () > 0) + { + static OSType subType = 0; + static OSType manuf = 0; + static bool once = true; + if (once) + { + once = false; + short prevResFile = CurResFile (); + CFBundleRef bundle = GetCurrentBundle (); + if (!bundle) + return; + + short resourceFileID = CFBundleOpenBundleResourceMap (bundle); + UseResFile (resourceFileID); + Handle h = Get1Resource ('thng', 9000); + if (h) + { + HLock (h); + OSType* hPtr = (OSType*)*h; + subType = hPtr[1]; + manuf = hPtr[2]; + HUnlock (h); + } + UseResFile (prevResFile); + CFRelease (bundle); + } + inDescArray[0].componentType = kAudioUnitCarbonViewComponentType; + inDescArray[0].componentSubType = subType; + inDescArray[0].componentManufacturer = manuf; + inDescArray[0].componentFlags = 0; + inDescArray[0].componentFlagsMask = 0; + } +#endif +} + +//------------------------------------------------------------------------ +Float64 AUWrapper::GetLatency () +{ + Float64 latencyInSeconds = 0.0; + if (audioProcessor) + latencyInSeconds = audioProcessor->getLatencySamples () / getSampleRate (); + return latencyInSeconds; +} + +//------------------------------------------------------------------------ +Float64 AUWrapper::GetTailTime () +{ + Float64 tailTimeInSeconds = 0.0; + if (audioProcessor) + tailTimeInSeconds = audioProcessor->getTailSamples () / getSampleRate (); + return tailTimeInSeconds; +} + +//------------------------------------------------------------------------ +// MusicDeviceBase +//------------------------------------------------------------------------ +ComponentResult AUWrapper::StartNote (MusicDeviceInstrumentID inInstrument, + MusicDeviceGroupID inGroupID, + NoteInstanceID* outNoteInstanceID, UInt32 inOffsetSampleFrame, + const MusicDeviceNoteParams& inParams) +{ + NoteInstanceID noteID = ((UInt8)inParams.mPitch) | ((noteCounter++) << 8); + + Event e = {0}; + + e.type = Event::kNoteOnEvent; + e.noteOn.pitch = inParams.mPitch; + e.noteOn.velocity = inParams.mVelocity / 127.; + e.noteOn.noteId = noteID; + e.sampleOffset = inOffsetSampleFrame; + + // The Group ID is the channel with a standard midi device + e.noteOn.channel = inGroupID; + + eventList.addEvent (e); + if (outNoteInstanceID) + *outNoteInstanceID = noteID; + return noErr; +} + +//------------------------------------------------------------------------ +ComponentResult AUWrapper::StopNote (MusicDeviceGroupID inGroupID, NoteInstanceID inNoteInstanceID, + UInt32 inOffsetSampleFrame) +{ + UInt32 pitch = inNoteInstanceID & 0xFF; + Event e = {0}; + e.type = Event::kNoteOffEvent; + e.noteOn.pitch = pitch; + e.noteOn.velocity = 0; + e.noteOn.noteId = inNoteInstanceID; + e.sampleOffset = inOffsetSampleFrame; + + // The Group ID is the channel with a standard midi device + e.noteOff.channel = inGroupID; + + eventList.addEvent (e); + return noErr; +} + +//-------------------------------------------------------------------------------------------- +OSStatus AUWrapper::GetInstrumentCount (UInt32& outInstCount) const +{ + outInstCount = 1; + return noErr; +} + +//------------------------------------------------------------------------ +// AUMIDIBase +//------------------------------------------------------------------------ +OSStatus AUWrapper::HandleNonNoteEvent (UInt8 status, UInt8 channel, UInt8 data1, UInt8 data2, + UInt32 inStartFrame) +{ + OSStatus result = noErr; + + if (status == kPolyPressure) // kMidiMessage_PolyPressure + { + Event e = {0}; + e.type = Event::kPolyPressureEvent; + e.polyPressure.channel = channel; + e.polyPressure.pitch = data1; + e.polyPressure.pressure = data2 / 127.; + e.sampleOffset = inStartFrame; + + eventList.addEvent (e); + return result; + } + + if (!midiMapping) + return result; + + ParamID pid = 0; + ParamValue value = 0; + CtrlNumber cn = -1; + bool prgChange = false; + + switch (status) + { + case kPitchBendStatus: // kMidiMessage_PitchWheel + { + cn = kPitchBend; + unsigned short _14bit; + _14bit = (unsigned short)data2; + _14bit <<= 7; + _14bit |= (unsigned short)data1; + value = (double)_14bit / (double)0x3FFF; + break; + } + case kAfterTouchStatus: // kMidiMessage_ChannelPressure + { + cn = kAfterTouch; + value = data1 / 127.f; + break; + } + case kController: // kMidiMessage_ControlChange + { + cn = data1; + value = data2 / 127.f; + break; + } + case kProgramChangeStatus: // kMidiMessage_ProgramChange + { + pid = programChangeParameters[channel]; + if (pid != kNoParamId) + { + ParameterInfo paramInfo = {0}; + if (editController->getParameterInfo (pid, paramInfo) == kResultTrue) + { + if (paramInfo.stepCount > 0 && data1 <= paramInfo.stepCount) + { + value = (ParamValue)data1 / (ParamValue)paramInfo.stepCount; + prgChange = true; + } + } + } + } + } + if (prgChange || (cn >= 0 && (midiMapping->getMidiControllerAssignment (0, channel, cn, pid) == + kResultTrue))) + { + beginEdit (pid); + performEdit (pid, value); + endEdit (pid); + + // make sure that our edit controller get the changes + int32 index; + IParamValueQueue* vq = outputParamChanges.addParameterData (pid, index); + if (vq) + vq->addPoint (inStartFrame, value, index); + } + return result; +} + +//------------------------------------------------------------------------ +// IComponentHandler +//------------------------------------------------------------------------ +tresult PLUGIN_API AUWrapper::beginEdit (ParamID tag) +{ + AudioUnitEvent auEvent; + auEvent.mArgument.mParameter.mAudioUnit = GetComponentInstance (); + auEvent.mArgument.mParameter.mParameterID = tag; + auEvent.mArgument.mParameter.mScope = kAudioUnitScope_Global; + auEvent.mArgument.mParameter.mElement = 0; + auEvent.mEventType = kAudioUnitEvent_BeginParameterChangeGesture; + AUEventListenerNotify (paramListenerRef, NULL, &auEvent); + + return kResultTrue; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API AUWrapper::performEdit (ParamID tag, ParamValue valueNormalized) +{ + AudioUnitParameter sPar = {GetComponentInstance (), tag, kAudioUnitScope_Global, 0}; + AUParameterSet (paramListenerRef, NULL, &sPar, valueNormalized, 0); + + AudioUnitEvent auEvent; + auEvent.mArgument.mParameter.mAudioUnit = GetComponentInstance (); + auEvent.mArgument.mParameter.mParameterID = tag; + auEvent.mArgument.mParameter.mScope = kAudioUnitScope_Global; + auEvent.mArgument.mParameter.mElement = 0; + auEvent.mEventType = kAudioUnitEvent_ParameterValueChange; + AUEventListenerNotify (paramListenerRef, NULL, &auEvent); + + return kResultTrue; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API AUWrapper::endEdit (ParamID tag) +{ + AudioUnitEvent auEvent; + auEvent.mArgument.mParameter.mAudioUnit = GetComponentInstance (); + auEvent.mArgument.mParameter.mParameterID = tag; + auEvent.mArgument.mParameter.mScope = kAudioUnitScope_Global; + auEvent.mArgument.mParameter.mElement = 0; + auEvent.mEventType = kAudioUnitEvent_EndParameterChangeGesture; + AUEventListenerNotify (paramListenerRef, NULL, &auEvent); + + return kResultTrue; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API AUWrapper::restartComponent (int32 flags) +{ + tresult result = kNotImplemented; + + if (flags & kParamValuesChanged) + { + syncParameterValues (); + result = kResultTrue; + } + + if (flags & kParamTitlesChanged) + { + cacheParameterValues (); + result = kResultTrue; + PropertyChanged (kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0); + } + + if (flags & kLatencyChanged) + { + AudioUnitEvent auEvent; + auEvent.mArgument.mProperty.mAudioUnit = GetComponentInstance (); + auEvent.mArgument.mProperty.mPropertyID = kAudioUnitProperty_Latency; + auEvent.mArgument.mProperty.mScope = kAudioUnitScope_Global; + auEvent.mArgument.mProperty.mElement = 0; + auEvent.mEventType = kAudioUnitEvent_PropertyChange; + AUEventListenerNotify (paramListenerRef, NULL, &auEvent); + result = kResultTrue; + } + // TODO: finish restartComponent implementation + return result; +} + +//------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------ +void AUWrapper::syncParameterValues () +{ + if (editController) + { + for (int32 i = 0; i < editController->getParameterCount (); i++) + { + ParameterInfo pi; + editController->getParameterInfo (i, pi); + if (pi.flags & ParameterInfo::kIsBypass) + { + bypassParamID = pi.id; + } + else + { + ParamValue value = editController->getParamNormalized (pi.id); + GlobalScope ().GetElement (0)->SetParameter (pi.id, value); + } + } + } +} + +//------------------------------------------------------------------------ +void AUWrapper::cacheParameterValues () +{ + clearParameterValueCache (); + + if (editController) + { + FGuard guard (parameterCacheChanging); + + FUnknownPtr unitInfoController (editController); + if (unitInfoController) + { + if (unitInfos.empty ()) + buildUnitInfos (unitInfoController, unitInfos); + } + + // This must be cached! + for (int32 i = 0; i < editController->getParameterCount (); i++) + { + AudioUnitParameterInfo auInfo; + ParameterInfo pi; + editController->getParameterInfo (i, pi); + + int32 clumpIdx = 0; // Zero should be reserved for values that are not clumped + + // Build clump group if possible + if (unitInfoController) + { + String fullUnitName; + ConstString separator (STR (".")); + bool insertSeparator = false; + + UnitInfoMap::const_iterator it = unitInfos.find (pi.unitId); + while (it != unitInfos.end () && it->second.id != kRootUnitId) + { + ConstString unitName (it->second.name); + if (unitName.length () > 0) + { + if (insertSeparator) + fullUnitName.insertAt (0, separator); + insertSeparator = true; + fullUnitName.insertAt (0, unitName); + } + it = unitInfos.find (it->second.parentUnitId); + } + if (!fullUnitName.isEmpty ()) + { + bool insertClump = true; + clumpIdx = 1; + for (int32 i = 0; i < clumpGroups.size (); i++) + { + const String& str = clumpGroups.at (i); + if (str.compare (fullUnitName) == 0) + { + insertClump = false; + break; + } + + ++clumpIdx; + } + + if (insertClump) + clumpGroups.push_back (fullUnitName); + } + } + + auInfo.name[0] = 0; + auInfo.cfNameString = createCFStringFromString128 (pi.title); + auInfo.defaultValue = pi.defaultNormalizedValue; + auInfo.minValue = 0; + auInfo.maxValue = 1; + auInfo.unit = kAudioUnitParameterUnit_CustomUnit; + auInfo.unitName = createCFStringFromString128 (pi.units); + + // We release this ourselved when emptying the cache + auInfo.flags = + /*kAudioUnitParameterFlag_CFNameRelease |*/ kAudioUnitParameterFlag_HasCFNameString | + kAudioUnitParameterFlag_IsReadable | kAudioUnitParameterFlag_HasName; + + if (clumpIdx > 0) + AUBase::HasClump (auInfo, clumpIdx); + + if (!(pi.flags & ParameterInfo::kIsReadOnly)) + auInfo.flags |= kAudioUnitParameterFlag_IsWritable; + if (!(pi.flags & ParameterInfo::kCanAutomate)) + auInfo.flags |= kAudioUnitParameterFlag_NonRealTime; + if (pi.stepCount == 1) + auInfo.unit = kAudioUnitParameterUnit_Boolean; + + cachedParameterInfos[pi.id] = auInfo; + } + } +} + +//------------------------------------------------------------------------ +void AUWrapper::clearParameterValueCache () +{ + FGuard guard (parameterCacheChanging); + + for (CachedParameterInfoMap::const_iterator it = cachedParameterInfos.begin (), + end = cachedParameterInfos.end (); + it != end; ++it) + { + CFRelease (it->second.cfNameString); + CFRelease (it->second.unitName); + } + cachedParameterInfos.clear (); +} + +//------------------------------------------------------------------------ +void AUWrapper::updateProcessContext () +{ + memset (&processContext, 0, sizeof (ProcessContext)); + processContext.sampleRate = getSampleRate (); + Float64 beat = 0; + Float64 tempo = 0; + if (CallHostBeatAndTempo (&beat, &tempo) == noErr) + { + processContext.state |= + ProcessContext::kTempoValid | ProcessContext::kProjectTimeMusicValid; + processContext.tempo = tempo; + processContext.projectTimeMusic = beat; + } + UInt32 deltaSampleOffsetToNextBeat = 0; + Float32 timeSigNumerator = 0; + UInt32 timeSigDenominator = 0; + Float64 currentMeasureDownBeat = 0; + if (CallHostMusicalTimeLocation (&deltaSampleOffsetToNextBeat, &timeSigNumerator, + &timeSigDenominator, ¤tMeasureDownBeat) == noErr) + { + processContext.state |= ProcessContext::kTimeSigValid | ProcessContext::kBarPositionValid | + ProcessContext::kClockValid; + processContext.timeSigNumerator = timeSigNumerator; + processContext.timeSigDenominator = timeSigDenominator; + processContext.samplesToNextClock = deltaSampleOffsetToNextBeat; + processContext.barPositionMusic = currentMeasureDownBeat; + } + Boolean isPlaying; + Boolean transportStateChanged; + Float64 currentSampleInTimeLine; + Boolean isCycling; + Float64 cycleStartBeat; + Float64 cycleEndBeat; + if (CallHostTransportState (&isPlaying, &transportStateChanged, ¤tSampleInTimeLine, + &isCycling, &cycleStartBeat, &cycleEndBeat) == noErr) + { + processContext.state |= ProcessContext::kCycleValid; + processContext.cycleStartMusic = cycleStartBeat; + processContext.cycleEndMusic = cycleEndBeat; + processContext.projectTimeSamples = currentSampleInTimeLine; + if (isPlaying) + processContext.state |= ProcessContext::kPlaying; + if (isCycling) + processContext.state |= ProcessContext::kCycleActive; + } +} + +//------------------------------------------------------------------------ +void AUWrapper::onTimer (Timer* timer) +{ + ParamID pid; + ParamValue value; + int32 sampleOffset; + while (outputParamTransfer.getNextChange (pid, value, sampleOffset)) + { + GlobalScope ().GetElement (0)->SetParameter (pid, value); + AudioUnitParameter auParam = {0}; + auParam.mAudioUnit = GetComponentInstance (); + auParam.mElement = 0; + auParam.mScope = kAudioUnitScope_Global; + auParam.mParameterID = pid; + AUParameterListenerNotify (paramListenerRef, 0, &auParam); + editController->setParamNormalized (pid, value); + } + if (isBypassed) + { + ProcessData bypassData = processData; + bypassData.numSamples = 0; + processParamChanges.clearQueue (); + transferParamChanges.transferChangesTo (processParamChanges); + if (processParamChanges.getParameterCount () > 0) + { + audioProcessor->process (bypassData); + outputParamTransfer.transferChangesFrom (outputParamChanges); + outputParamChanges.clearQueue (); + } + } +} + +//------------------------------------------------------------------------ +bool AUWrapper::validateChannelPair (int inChannelsIn, int inChannelsOut, const AUChannelInfo* info, + UInt32 numChanInfo) const +{ + // we've the following cases (some combinations) to test here: + /* + >0 An explicit number of channels on either side + 0 that side (generally input!) has no elements + -1 wild card: + -1,-1 any num channels as long as same channels on in and out + -1,-2 any num channels channels on in and out - special meaning + -2+ indicates total num channs AU can handle + - elements configurable to any num channels, + - element count in scope must be writable + */ + + // now chan layout can contain -1 for either scope (ie. doesn't care) + for (unsigned int i = 0; i < numChanInfo; ++i) + { + // less than zero on both sides - check for special attributes + if ((info[i].inChannels < 0) && (info[i].outChannels < 0)) + { + // these are our wild card matches + if (info[i].inChannels == -1 && info[i].outChannels == -1) + { + if (inChannelsIn && inChannelsOut) + { + if (inChannelsOut == inChannelsIn) + return true; + } + else + return true; // if one of these is zero, then a -1 means any + } + else if ((info[i].inChannels == -1 && info[i].outChannels == -2) || + (info[i].inChannels == -2 && info[i].outChannels == -1)) + { + return true; + } + // these are our total num channels matches + // element count MUST be writable + else + { + bool outWrite = false; + bool inWrite = false; + // IsElementCountWritable (kAudioUnitScope_Output, outWrite); + // IsElementCountWritable (kAudioUnitScope_Input, inWrite); + if (inWrite && outWrite) + { + if ((inChannelsOut <= abs (info[i].outChannels)) && + (inChannelsIn <= abs (info[i].inChannels))) + { + return true; + } + } + } + } + + // special meaning on input, specific num on output + else if (info[i].inChannels < 0) + { + if (info[i].outChannels == inChannelsOut) + { + // can do any in channels + if (info[i].inChannels == -1) + { + return true; + } + // total chans on input + else + { + bool inWrite = false; + // IsElementCountWritable (kAudioUnitScope_Input, inWrite); + if (inWrite && (inChannelsIn <= abs (info[i].inChannels))) + { + return true; + } + } + } + } + + // special meaning on output, specific num on input + else if (info[i].outChannels < 0) + { + if (info[i].inChannels == inChannelsIn) + { + // can do any out channels + if (info[i].outChannels == -1) + { + return true; + } + // total chans on output + else + { + bool outWrite = false; + // IsElementCountWritable (kAudioUnitScope_Output, outWrite); + if (outWrite && (inChannelsOut <= abs (info[i].outChannels))) + { + return true; + } + } + } + } + + // both chans in struct >= 0 - thus has to explicitly match + else if ((info[i].inChannels == inChannelsIn) && (info[i].outChannels == inChannelsOut)) + { + return true; + } + + // now check to see if a wild card on the args (inChannelsIn or inChannelsOut chans is zero) + // is found + // tells us to match just one side of the scopes + else if (inChannelsIn == 0) + { + if (info[i].outChannels == inChannelsOut) + { + return true; + } + } + else if (inChannelsOut == 0) + { + if (info[i].inChannels == inChannelsIn) + { + return true; + } + } + } + + return false; +} + +//------------------------------------------------------------------------ +bool AUWrapper::getProgramListAndUnit (int32 midiChannel, UnitID& unitId, + ProgramListID& programListId) +{ + programListId = kNoProgramListId; + unitId = -1; + + IUnitInfo* unitInfo = NULL; + + if (editController && + editController->queryInterface (IUnitInfo::iid, (void**)&unitInfo) == kResultTrue && + unitInfo) + { + if (unitInfo->getUnitByBus (kEvent, kInput, 0, midiChannel, unitId) == kResultTrue) + { + int32 unitCount = unitInfo->getUnitCount (); + for (int32 i = 0; i < unitCount; i++) + { + UnitInfo unitInfoStruct = {0}; + if (unitInfo->getUnitInfo (i, unitInfoStruct) == kResultTrue) + { + if (unitId == unitInfoStruct.id) + { + programListId = unitInfoStruct.programListId; + unitInfo->release (); + return programListId != kNoProgramListId; + } + } + } + } + + unitInfo->release (); + } + return false; +} + +#if !CA_USE_AUDIO_PLUGIN_ONLY + +// copied from AUDispatch.cpp +#if __LP64__ +// comp instance, parameters in forward order +#define PARAM(_typ, _name, _index, _nparams) _typ _name = *(_typ*)¶ms->params[_index + 1]; +#else +// parameters in reverse order, then comp instance +#define PARAM(_typ, _name, _index, _nparams) \ + _typ _name = *(_typ*)¶ms->params[_nparams - 1 - _index]; +#endif + +//------------------------------------------------------------------------ +ComponentResult AUWrapper::ComponentEntryDispatch (ComponentParameters* params, AUWrapper* This) +{ + OSStatus result = noErr; + + switch (params->what) + { + case kAudioUnitSetPropertySelect: + { + PARAM (AudioUnitPropertyID, inID, 0, 5); + + if (inID == kAudioUnitProperty_ClassInfoFromDocument) + { + PARAM (AudioUnitScope, inScope, 1, 5); + PARAM (AudioUnitElement, inElement, 2, 5); + PARAM (const void*, inData, 3, 5); + PARAM (UInt32, inDataSize, 4, 5); + + ca_require (inDataSize == sizeof (CFPropertyListRef*), InvalidPropertyValue); + ca_require (inScope == kAudioUnitScope_Global, InvalidScope); + result = This->restoreState (*(CFPropertyListRef*)inData, true); + return result; + } + break; + } + } + return AUWRAPPER_BASE_CLASS::ComponentEntryDispatch (params, This); + +InvalidScope: + return kAudioUnitErr_InvalidScope; +InvalidPropertyValue: + return kAudioUnitErr_InvalidPropertyValue; +} +#endif + +//------------------------------------------------------------------------ +//------------------------------------------------------------------------ +//------------------------------------------------------------------------ +class CleanupMemory +{ +public: + CleanupMemory () {} + ~CleanupMemory () + { + if (channelInfos) + { + delete[] channelInfos; + channelInfos = 0; + } + } +}; + +CleanupMemory _gCleanupMemory; +CleanupMemory* gCleanupMemory = &_gCleanupMemory; + +#if __MAC_OS_X_VERSION_MAX_ALLOWED == 1060 // if compiling against the 10.6 sdk +//------------------------------------------------------------------------ +// COMPONENT_ENTRY(AUWrapper) +//------------------------------------------------------------------------ +extern "C" { +ComponentResult AUWrapperEntry (ComponentParameters* params, AUWrapper* obj); +__attribute__ ((visibility ("default"))) ComponentResult AUWrapperEntry ( + ComponentParameters* params, AUWrapper* obj) +{ + return ComponentEntryPoint::Dispatch (params, obj); +} +} + +#else +struct AUWrapperLookup +{ + static AudioComponentMethod Lookup (SInt16 selector) + { + // ProcessBufferLists not supported, so don't try any of the process methods + if (selector == kAudioUnitProcessSelect || selector == kAudioUnitProcessMultipleSelect) + return NULL; + AudioComponentMethod method = AUBaseProcessLookup::Lookup (selector); + if (method) + return method; + method = AUMusicLookup::Lookup (selector); + if (method) + return method; + method = AUBaseProcessMultipleLookup::Lookup (selector); + if (method) + return method; + return NULL; + } +}; +template +class AUFactory : public APFactory +{ +}; + +//------------------------------------------------------------------------ +// new entry method +extern "C" { +void* AUWrapperFactory (const AudioComponentDescription* inDesc); +__attribute__ ((visibility ("default"))) void* AUWrapperFactory ( + const AudioComponentDescription* inDesc) +{ + return AUFactory::Factory (inDesc); +} +} // extern "C" + +#if !CA_USE_AUDIO_PLUGIN_ONLY +//------------------------------------------------------------------------ +// old entry method +extern "C" { +ComponentResult AUWrapperEntry (ComponentParameters* params, AUWrapper* obj); +__attribute__ ((visibility ("default"))) ComponentResult AUWrapperEntry ( + ComponentParameters* params, AUWrapper* obj) +{ + return ComponentEntryPoint::Dispatch (params, obj); +} +} +#endif +#endif + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg + +/// \endcond diff --git a/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/auwrapper_prefix.pch b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/auwrapper_prefix.pch new file mode 100644 index 000000000..6f7d7573c --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/auwrapper_prefix.pch @@ -0,0 +1,38 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/auwrapper/auwrapper_prefix.pch +// Created by : Steinberg, 12/2007 +// Description : VST 3 -> AU Wrapper +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include +#include diff --git a/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/config/ausdkpath.xcconfig b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/config/ausdkpath.xcconfig new file mode 100644 index 000000000..f99649c47 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/config/ausdkpath.xcconfig @@ -0,0 +1,38 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : ausdkpath.xcconfig +// Created by : Steinberg, 5/24/12 +// Description : Xcode configuration file to specify paths to the AU SDK files, VST 3 -> AU Wrapper +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +// If you are building with Xcode >= 4.x please add the path to your downloaded Audio Tools for Xcode +CUSTOM_AU_SDK_PATH=/Applications/Xcode.app/Contents/Developer/Extras/CoreAudio/ // AUWRAPPER_CHANGE diff --git a/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/config/auwrapper.xcconfig b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/config/auwrapper.xcconfig new file mode 100644 index 000000000..5b9244cb7 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/config/auwrapper.xcconfig @@ -0,0 +1,44 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : auwrapper.xcconfig +// Created by : Steinberg, 5/24/12 +// Description : Xcode configuration file to specify paths to the AU SDK files, VST 3 -> AU Wrapper +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "../../../../../base/mac/config/libc++base" +#include "ausdkpath" + +PRODUCT_NAME = auwrapper + +HEADER_SEARCH_PATHS = ../../../../ $(CUSTOM_AU_SDK_PATH)/** $(DEVELOPER_DIR)/Examples/CoreAudio/** $(DEVELOPER_DIR)/Extras/CoreAudio/** $(DEVELOPER_DIR)/Extras/CoreAudio/AudioUnits/AUPublic/AUViewBase/** ../../../../libraries/source/CoreAudio/** +GCC_PREFIX_HEADER = auwrapper_prefix.pch +GCC_PRECOMPILE_PREFIX_HEADER = YES diff --git a/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/config/auwrapper_debug.xcconfig b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/config/auwrapper_debug.xcconfig new file mode 100644 index 000000000..f2f3fc2a4 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/config/auwrapper_debug.xcconfig @@ -0,0 +1,40 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : ausdkpath_debug.xcconfig +// Created by : Steinberg, 5/24/12 +// Description : Xcode configuration file to specify paths to the AU SDK files, VST 3 -> AU Wrapper +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "../../../../../base/mac/config/debug" + +GCC_OPTIMIZATION_LEVEL = 0 +DEPLOYMENT_POSTPROCESSING = NO diff --git a/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/config/auwrapper_release.xcconfig b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/config/auwrapper_release.xcconfig new file mode 100644 index 000000000..c7ba15db7 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/config/auwrapper_release.xcconfig @@ -0,0 +1,40 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : auwrapper_release.xcconfig +// Created by : Steinberg, 5/24/12 +// Description : Xcode configuration file to specify paths to the AU SDK files, VST 3 -> AU Wrapper +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "../../../../../base/mac/config/release" + +GCC_OPTIMIZATION_LEVEL = 3 +DEPLOYMENT_POSTPROCESSING = NO diff --git a/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/generateCocoaClassNamePrefix.rb b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/generateCocoaClassNamePrefix.rb new file mode 100755 index 000000000..b2655064c --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/auwrapper/generateCocoaClassNamePrefix.rb @@ -0,0 +1,23 @@ +#!/usr/bin/ruby + +require 'time' + +cocoaClassPrefixDir=ARGV[0] + +cocoaClassPrefixDir = ARGV[0] +if cocoaClassPrefixDir == nil + $stdout << "Cannot resolve output directory\n" + exit(-1) +end + +$stdout << "Generating new class prefix for Objective-C classes in #{cocoaClassPrefixDir}\n" +File.open("#{cocoaClassPrefixDir}/aucocoaclassprefix.h", "w+") do |stream| + + t = Time.now.to_i + t.round + id = t.to_s + stream << "#define SMTG_AU_NAMESPACE\t" + stream << "SMTGAUCocoa#{id}_\n" + +end + diff --git a/source/includes/vst3sdk/public.sdk/source/vst/hosting/eventlist.cpp b/source/includes/vst3sdk/public.sdk/source/vst/hosting/eventlist.cpp new file mode 100644 index 000000000..c783a4069 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/hosting/eventlist.cpp @@ -0,0 +1,105 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/hosting/eventlist.cpp +// Created by : Steinberg, 03/05/2008. +// Description : VST 3 event list implementation +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "eventlist.h" + +namespace Steinberg { +namespace Vst { + +//----------------------------------------------------------------------------- +IMPLEMENT_FUNKNOWN_METHODS (EventList, IEventList, IEventList::iid) + +//----------------------------------------------------------------------------- +EventList::EventList (int32 maxSize) : events (nullptr), maxSize (maxSize), fillCount (0) +{ + FUNKNOWN_CTOR + setMaxSize (maxSize); +} + +//----------------------------------------------------------------------------- +EventList::~EventList () +{ + setMaxSize (0); + FUNKNOWN_DTOR +} + +//----------------------------------------------------------------------------- +void EventList::setMaxSize (int32 _maxSize) +{ + if (events) + { + delete[] events; + events = nullptr; + } + if (_maxSize > 0) + events = new Event[_maxSize]; + maxSize = _maxSize; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API EventList::getEvent (int32 index, Event& e) +{ + if (fillCount > index) + { + memcpy (&e, &events[index], sizeof (Event)); + return kResultTrue; + } + return kResultFalse; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API EventList::addEvent (Event& e) +{ + if (maxSize > fillCount) + { + memcpy (&events[fillCount], &e, sizeof (Event)); + fillCount++; + return kResultTrue; + } + return kResultFalse; +} + +//----------------------------------------------------------------------------- +Event* EventList::getEventByIndex (int32 index) +{ + if (index < fillCount) + return &events[index]; + return nullptr; +} + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/hosting/eventlist.h b/source/includes/vst3sdk/public.sdk/source/vst/hosting/eventlist.h new file mode 100644 index 000000000..224d69747 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/hosting/eventlist.h @@ -0,0 +1,73 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/hosting/eventlist.h +// Created by : Steinberg, 03/05/2008. +// Description : VST 3 event list implementation +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "pluginterfaces/vst/ivstevents.h" + +namespace Steinberg { +namespace Vst { + +//------------------------------------------------------------------------ +/** Implementation's example of IEventList. +\ingroup sdkBase +*/ +class EventList : public IEventList +{ +public: + EventList (int32 maxSize = 50); + virtual ~EventList (); + + void clear () { fillCount = 0; } + + int32 PLUGIN_API getEventCount () SMTG_OVERRIDE { return fillCount; } + tresult PLUGIN_API getEvent (int32 index, Event& e) SMTG_OVERRIDE; + tresult PLUGIN_API addEvent (Event& e) SMTG_OVERRIDE; + + Event* getEventByIndex (int32 index); + void setMaxSize (int32 maxSize); + +//------------------------------------------------------------------------ + DECLARE_FUNKNOWN_METHODS +protected: + Event* events; + int32 maxSize; + int32 fillCount; +}; + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/hosting/hostclasses.cpp b/source/includes/vst3sdk/public.sdk/source/vst/hosting/hostclasses.cpp new file mode 100644 index 000000000..988b05079 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/hosting/hostclasses.cpp @@ -0,0 +1,316 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/hosting/hostclasses.cpp +// Created by : Steinberg, 03/05/2008. +// Description : VST 3 hostclasses, example implementations for IHostApplication, IAttributeList and IMessage +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "hostclasses.h" + +#include + +namespace Steinberg { +namespace Vst { + +//----------------------------------------------------------------------------- +tresult PLUGIN_API HostApplication::getName (String128 name) +{ + String str ("My VST3 HostApplication"); + str.copyTo16 (name, 0, 127); + return kResultTrue; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API HostApplication::createInstance (TUID cid, TUID _iid, void** obj) +{ + FUID classID (cid); + FUID interfaceID (_iid); + if (classID == IMessage::iid && interfaceID == IMessage::iid) + { + *obj = new HostMessage; + return kResultTrue; + } + else if (classID == IAttributeList::iid && interfaceID == IAttributeList::iid) + { + *obj = new HostAttributeList; + return kResultTrue; + } + *obj = 0; + return kResultFalse; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API HostApplication::queryInterface (const char* _iid, void** obj) +{ + QUERY_INTERFACE (_iid, obj, FUnknown::iid, IHostApplication) + QUERY_INTERFACE (_iid, obj, IHostApplication::iid, IHostApplication) + *obj = 0; + return kResultFalse; +} + +//----------------------------------------------------------------------------- +uint32 PLUGIN_API HostApplication::addRef () +{ + return 1; +} + +//----------------------------------------------------------------------------- +uint32 PLUGIN_API HostApplication::release () +{ + return 1; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +IMPLEMENT_FUNKNOWN_METHODS (HostMessage, IMessage, IMessage::iid) +//----------------------------------------------------------------------------- +HostMessage::HostMessage () : messageId (0), attributeList (0) +{ + FUNKNOWN_CTOR +} + +//----------------------------------------------------------------------------- +HostMessage::~HostMessage () +{ + setMessageID (0); + if (attributeList) + attributeList->release (); + FUNKNOWN_DTOR +} + +//----------------------------------------------------------------------------- +const char* PLUGIN_API HostMessage::getMessageID () +{ + return messageId; +} + +//----------------------------------------------------------------------------- +void PLUGIN_API HostMessage::setMessageID (const char* mid) +{ + if (messageId) + delete[] messageId; + messageId = 0; + if (mid) + { + size_t len = strlen (mid) + 1; + messageId = new char[len]; + strcpy (messageId, mid); + } +} + +//----------------------------------------------------------------------------- +IAttributeList* PLUGIN_API HostMessage::getAttributes () +{ + if (!attributeList) + attributeList = new HostAttributeList; + return attributeList; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +class HostAttribute +{ +public: + enum Type + { + kInteger, + kFloat, + kString, + kBinary + }; + + HostAttribute (int64 value) : size (0), type (kInteger) { v.intValue = value; } + HostAttribute (double value) : size (0), type (kFloat) { v.floatValue = value; } + HostAttribute (const TChar* value, uint32 size) : size (size), type (kString) + { + v.stringValue = new TChar[size]; + memcpy (v.stringValue, value, size * sizeof (TChar)); + } + HostAttribute (const void* value, uint32 size) : size (size), type (kBinary) + { + v.binaryValue = new char[size]; + memcpy (v.binaryValue, value, size); + } + ~HostAttribute () + { + if (size) + delete[] v.binaryValue; + } + + int64 intValue () const { return v.intValue; } + double floatValue () const { return v.floatValue; } + const TChar* stringValue (uint32& stringSize) + { + stringSize = size; + return v.stringValue; + } + const void* binaryValue (uint32& binarySize) + { + binarySize = size; + return v.binaryValue; + } + + Type getType () const { return type; } + +protected: + union v + { + int64 intValue; + double floatValue; + TChar* stringValue; + char* binaryValue; + } v; + uint32 size; + Type type; +}; + +typedef std::map::iterator mapIterator; + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +IMPLEMENT_FUNKNOWN_METHODS (HostAttributeList, IAttributeList, IAttributeList::iid) +//----------------------------------------------------------------------------- +HostAttributeList::HostAttributeList () +{ + FUNKNOWN_CTOR +} + +//----------------------------------------------------------------------------- +HostAttributeList::~HostAttributeList () +{ + std::map::reverse_iterator it = list.rbegin (); + while (it != list.rend ()) + { + delete it->second; + it++; + } + FUNKNOWN_DTOR +} + +//----------------------------------------------------------------------------- +void HostAttributeList::removeAttrID (AttrID aid) +{ + mapIterator it = list.find (aid); + if (it != list.end ()) + { + delete it->second; + list.erase (it); + } +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API HostAttributeList::setInt (AttrID aid, int64 value) +{ + removeAttrID (aid); + list[aid] = new HostAttribute (value); + return kResultTrue; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API HostAttributeList::getInt (AttrID aid, int64& value) +{ + mapIterator it = list.find (aid); + if (it != list.end () && it->second) + { + value = it->second->intValue (); + return kResultTrue; + } + return kResultFalse; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API HostAttributeList::setFloat (AttrID aid, double value) +{ + removeAttrID (aid); + list[aid] = new HostAttribute (value); + return kResultTrue; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API HostAttributeList::getFloat (AttrID aid, double& value) +{ + mapIterator it = list.find (aid); + if (it != list.end () && it->second) + { + value = it->second->floatValue (); + return kResultTrue; + } + return kResultFalse; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API HostAttributeList::setString (AttrID aid, const TChar* string) +{ + removeAttrID (aid); + list[aid] = new HostAttribute (string, String (const_cast (string)).length ()); + return kResultTrue; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API HostAttributeList::getString (AttrID aid, TChar* string, uint32 size) +{ + mapIterator it = list.find (aid); + if (it != list.end () && it->second) + { + uint32 stringSize = 0; + const TChar* _string = it->second->stringValue (stringSize); + memcpy (string, _string, std::min (stringSize, size) * sizeof (TChar)); + return kResultTrue; + } + return kResultFalse; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API HostAttributeList::setBinary (AttrID aid, const void* data, uint32 size) +{ + removeAttrID (aid); + list[aid] = new HostAttribute (data, size); + return kResultTrue; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API HostAttributeList::getBinary (AttrID aid, const void*& data, uint32& size) +{ + mapIterator it = list.find (aid); + if (it != list.end () && it->second) + { + data = it->second->binaryValue (size); + return kResultTrue; + } + size = 0; + return kResultFalse; +} +} +} // namespace diff --git a/source/includes/vst3sdk/public.sdk/source/vst/hosting/hostclasses.h b/source/includes/vst3sdk/public.sdk/source/vst/hosting/hostclasses.h new file mode 100644 index 000000000..b394c94ac --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/hosting/hostclasses.h @@ -0,0 +1,112 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/hosting/hostclasses.h +// Created by : Steinberg, 03/05/2008. +// Description : VST 3 hostclasses, example implementations for IHostApplication, IAttributeList and IMessage +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "pluginterfaces/vst/ivsthostapplication.h" + +#include "base/source/fstring.h" + +#include + +namespace Steinberg { +namespace Vst { + +//------------------------------------------------------------------------ +/** Implementation's example of IHostApplication. +\ingroup hostingBase +*/ +class HostApplication : public IHostApplication +{ +public: + HostApplication () { FUNKNOWN_CTOR } + virtual ~HostApplication () { FUNKNOWN_DTOR } + + tresult PLUGIN_API getName (String128 name) SMTG_OVERRIDE; + tresult PLUGIN_API createInstance (TUID cid, TUID _iid, void** obj) SMTG_OVERRIDE; + + DECLARE_FUNKNOWN_METHODS +}; + +class HostAttribute; +//------------------------------------------------------------------------ +/** Implementation's example of IAttributeList. +\ingroup hostingBase +*/ +class HostAttributeList : public IAttributeList +{ +public: + HostAttributeList (); + virtual ~HostAttributeList (); + + tresult PLUGIN_API setInt (AttrID aid, int64 value) SMTG_OVERRIDE; + tresult PLUGIN_API getInt (AttrID aid, int64& value) SMTG_OVERRIDE; + tresult PLUGIN_API setFloat (AttrID aid, double value) SMTG_OVERRIDE; + tresult PLUGIN_API getFloat (AttrID aid, double& value) SMTG_OVERRIDE; + tresult PLUGIN_API setString (AttrID aid, const TChar* string) SMTG_OVERRIDE; + tresult PLUGIN_API getString (AttrID aid, TChar* string, uint32 size) SMTG_OVERRIDE; + tresult PLUGIN_API setBinary (AttrID aid, const void* data, uint32 size) SMTG_OVERRIDE; + tresult PLUGIN_API getBinary (AttrID aid, const void*& data, uint32& size) SMTG_OVERRIDE; + + DECLARE_FUNKNOWN_METHODS +protected: + void removeAttrID (AttrID aid); + std::map list; +}; + +//------------------------------------------------------------------------ +/** Implementation's example of IMessage. +\ingroup hostingBase +*/ +class HostMessage : public IMessage +{ +public: + HostMessage (); + virtual ~HostMessage (); + + const char* PLUGIN_API getMessageID () SMTG_OVERRIDE; + void PLUGIN_API setMessageID (const char* messageID) SMTG_OVERRIDE; + IAttributeList* PLUGIN_API getAttributes () SMTG_OVERRIDE; + + DECLARE_FUNKNOWN_METHODS +protected: + char* messageId; + HostAttributeList* attributeList; +}; + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/hosting/module.cpp b/source/includes/vst3sdk/public.sdk/source/vst/hosting/module.cpp new file mode 100644 index 000000000..84b6d60e7 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/hosting/module.cpp @@ -0,0 +1,284 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/hosting/module.cpp +// Created by : Steinberg, 08/2016 +// Description : hosting module classes +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "module.h" +#include "stringconvert.h" +#include +#include + +//------------------------------------------------------------------------ +namespace VST3 { +namespace Hosting { + +//------------------------------------------------------------------------ +FactoryInfo::FactoryInfo (PFactoryInfo&& other) noexcept +{ + *this = std::move (other); +} + +//------------------------------------------------------------------------ +FactoryInfo& FactoryInfo::operator= (FactoryInfo&& other) noexcept +{ + info = std::move (other.info); + other.info = {}; + return *this; +} + +//------------------------------------------------------------------------ +FactoryInfo& FactoryInfo::operator= (PFactoryInfo&& other) noexcept +{ + info = std::move (other); + other = {}; + return *this; +} + +//------------------------------------------------------------------------ +std::string FactoryInfo::vendor () const noexcept +{ + return StringConvert::convert (info.vendor, PFactoryInfo::kNameSize); +} + +//------------------------------------------------------------------------ +std::string FactoryInfo::url () const noexcept +{ + return StringConvert::convert (info.url, PFactoryInfo::kURLSize); +} + +//------------------------------------------------------------------------ +std::string FactoryInfo::email () const noexcept +{ + return StringConvert::convert (info.email, PFactoryInfo::kEmailSize); +} + +//------------------------------------------------------------------------ +Steinberg::int32 FactoryInfo::flags () const noexcept +{ + return info.flags; +} + +//------------------------------------------------------------------------ +bool FactoryInfo::classesDiscardable () const noexcept +{ + return (info.flags & PFactoryInfo::kClassesDiscardable) != 0; +} + +//------------------------------------------------------------------------ +bool FactoryInfo::licenseCheck () const noexcept +{ + return (info.flags & PFactoryInfo::kLicenseCheck) != 0; +} + +//------------------------------------------------------------------------ +bool FactoryInfo::componentNonDiscardable () const noexcept +{ + return (info.flags & PFactoryInfo::kComponentNonDiscardable) != 0; +} + +//------------------------------------------------------------------------ +//------------------------------------------------------------------------ +//------------------------------------------------------------------------ +PluginFactory::PluginFactory (const PluginFactoryPtr& factory) noexcept : factory (factory) +{ +} + +//------------------------------------------------------------------------ +void PluginFactory::setHostContext (Steinberg::FUnknown* context) const noexcept +{ + if (auto f = Steinberg::FUnknownPtr (factory)) + f->setHostContext (context); +} + +//------------------------------------------------------------------------ +FactoryInfo PluginFactory::info () const noexcept +{ + Steinberg::PFactoryInfo i; + factory->getFactoryInfo (&i); + return FactoryInfo (std::move (i)); +} + +//------------------------------------------------------------------------ +uint32_t PluginFactory::classCount () const noexcept +{ + auto count = factory->countClasses (); + assert (count >= 0); + return static_cast (count); +} + +//------------------------------------------------------------------------ +PluginFactory::ClassInfos PluginFactory::classInfos () const noexcept +{ + auto count = classCount (); + ClassInfos classes; + classes.reserve (count); + auto f3 = Steinberg::FUnknownPtr (factory); + auto f2 = Steinberg::FUnknownPtr (factory); + Steinberg::PClassInfo ci; + Steinberg::PClassInfo2 ci2; + Steinberg::PClassInfoW ci3; + for (uint32_t i = 0; i < count; ++i) + { + if (f3 && f3->getClassInfoUnicode (i, &ci3) == Steinberg::kResultTrue) + classes.emplace_back (ci3); + else if (f2 && f2->getClassInfo2 (i, &ci2) == Steinberg::kResultTrue) + classes.emplace_back (ci2); + else if (factory->getClassInfo (i, &ci) == Steinberg::kResultTrue) + classes.emplace_back (ci); + auto& classInfo = classes.back (); + if (classInfo.vendor ().empty ()) + classInfo.get ().vendor = info ().vendor (); + } + return classes; +} + +//------------------------------------------------------------------------ +//------------------------------------------------------------------------ +//------------------------------------------------------------------------ +const UID& ClassInfo::ID () const noexcept +{ + return data.classID; +} + +//------------------------------------------------------------------------ +int32_t ClassInfo::cardinality () const noexcept +{ + return data.cardinality; +} + +//------------------------------------------------------------------------ +const std::string& ClassInfo::category () const noexcept +{ + return data.category; +} + +//------------------------------------------------------------------------ +const std::string& ClassInfo::name () const noexcept +{ + return data.name; +} + +//------------------------------------------------------------------------ +const std::string& ClassInfo::vendor () const noexcept +{ + return data.vendor; +} + +//------------------------------------------------------------------------ +const std::string& ClassInfo::version () const noexcept +{ + return data.version; +} + +//------------------------------------------------------------------------ +const std::string& ClassInfo::sdkVersion () const noexcept +{ + return data.sdkVersion; +} + +//------------------------------------------------------------------------ +const ClassInfo::SubCategories& ClassInfo::subCategories () const noexcept +{ + return data.subCategories; +} + +//------------------------------------------------------------------------ +Steinberg::uint32 ClassInfo::classFlags () const noexcept +{ + return data.classFlags; +} + +//------------------------------------------------------------------------ +ClassInfo::ClassInfo (const PClassInfo& info) noexcept +{ + data.classID = info.cid; + data.cardinality = info.cardinality; + data.category = StringConvert::convert (info.category, PClassInfo::kCategorySize); + data.name = StringConvert::convert (info.name, PClassInfo::kNameSize); +} + +//------------------------------------------------------------------------ +ClassInfo::ClassInfo (const PClassInfo2& info) noexcept +{ + data.classID = info.cid; + data.cardinality = info.cardinality; + data.category = StringConvert::convert (info.category, PClassInfo::kCategorySize); + data.name = StringConvert::convert (info.name, PClassInfo::kNameSize); + data.vendor = StringConvert::convert (info.vendor, PClassInfo2::kVendorSize); + data.version = StringConvert::convert (info.version, PClassInfo2::kVersionSize); + data.sdkVersion = StringConvert::convert (info.sdkVersion, PClassInfo2::kVersionSize); + parseSubCategories ( + StringConvert::convert (info.subCategories, PClassInfo2::kSubCategoriesSize)); + data.classFlags = info.classFlags; +} + +//------------------------------------------------------------------------ +ClassInfo::ClassInfo (const PClassInfoW& info) noexcept +{ + data.classID = info.cid; + data.cardinality = info.cardinality; + data.category = StringConvert::convert (info.category, PClassInfo::kCategorySize); + data.name = StringConvert::convert (info.name, PClassInfo::kNameSize); + data.vendor = StringConvert::convert (info.vendor, PClassInfo2::kVendorSize); + data.version = StringConvert::convert (info.version, PClassInfo2::kVersionSize); + data.sdkVersion = StringConvert::convert (info.sdkVersion, PClassInfo2::kVersionSize); + parseSubCategories ( + StringConvert::convert (info.subCategories, PClassInfo2::kSubCategoriesSize)); + data.classFlags = info.classFlags; +} + +//------------------------------------------------------------------------ +void ClassInfo::parseSubCategories (const std::string& str) noexcept +{ + std::stringstream stream (str); + std::string item; + while (std::getline (stream, item, '|')) + data.subCategories.emplace_back (move (item)); +} + +//------------------------------------------------------------------------ +std::string ClassInfo::subCategoriesString () const noexcept +{ + std::string result; + if (data.subCategories.empty ()) + return result; + result = data.subCategories[0]; + for (auto index = 1u; index < data.subCategories.size (); ++index) + result += "|" + data.subCategories[index]; + return result; +} + +//------------------------------------------------------------------------ +} // Hosting +} // VST3 diff --git a/source/includes/vst3sdk/public.sdk/source/vst/hosting/module.h b/source/includes/vst3sdk/public.sdk/source/vst/hosting/module.h new file mode 100644 index 000000000..94db0c591 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/hosting/module.h @@ -0,0 +1,193 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/hosting/module.h +// Created by : Steinberg, 08/2016 +// Description : hosting module classes +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "uid.h" +#include "pluginterfaces/base/ipluginbase.h" +#include +#include + +//------------------------------------------------------------------------ +namespace VST3 { +namespace Hosting { + +//------------------------------------------------------------------------ +class FactoryInfo +{ +public: +//------------------------------------------------------------------------ + using PFactoryInfo = Steinberg::PFactoryInfo; + + FactoryInfo () noexcept {} + ~FactoryInfo () noexcept {} + FactoryInfo (const FactoryInfo&) noexcept = default; + FactoryInfo (PFactoryInfo&&) noexcept; + FactoryInfo (FactoryInfo&&) noexcept = default; + FactoryInfo& operator= (const FactoryInfo&) noexcept = default; + FactoryInfo& operator= (FactoryInfo&&) noexcept; + FactoryInfo& operator= (PFactoryInfo&&) noexcept; + + std::string vendor () const noexcept; + std::string url () const noexcept; + std::string email () const noexcept; + Steinberg::int32 flags () const noexcept; + bool classesDiscardable () const noexcept; + bool licenseCheck () const noexcept; + bool componentNonDiscardable () const noexcept; + + PFactoryInfo& get () noexcept { return info; } +//------------------------------------------------------------------------ +private: + PFactoryInfo info {}; +}; + +//------------------------------------------------------------------------ +class ClassInfo +{ +public: +//------------------------------------------------------------------------ + using SubCategories = std::vector; + using PClassInfo = Steinberg::PClassInfo; + using PClassInfo2 = Steinberg::PClassInfo2; + using PClassInfoW = Steinberg::PClassInfoW; + +//------------------------------------------------------------------------ + ClassInfo () noexcept {} + explicit ClassInfo (const PClassInfo& info) noexcept; + explicit ClassInfo (const PClassInfo2& info) noexcept; + explicit ClassInfo (const PClassInfoW& info) noexcept; + ClassInfo (const ClassInfo&) = default; + ClassInfo& operator= (const ClassInfo&) = default; + ClassInfo (ClassInfo&&) = default; + ClassInfo& operator= (ClassInfo&&) = default; + + const UID& ID () const noexcept; + int32_t cardinality () const noexcept; + const std::string& category () const noexcept; + const std::string& name () const noexcept; + const std::string& vendor () const noexcept; + const std::string& version () const noexcept; + const std::string& sdkVersion () const noexcept; + const SubCategories& subCategories () const noexcept; + std::string subCategoriesString () const noexcept; + Steinberg::uint32 classFlags () const noexcept; + + struct Data + { + UID classID; + int32_t cardinality; + std::string category; + std::string name; + std::string vendor; + std::string version; + std::string sdkVersion; + SubCategories subCategories; + Steinberg::uint32 classFlags = 0; + }; + + Data& get () noexcept { return data; } +//------------------------------------------------------------------------ +private: + void parseSubCategories (const std::string& str) noexcept; + Data data {}; +}; + +//------------------------------------------------------------------------ +class PluginFactory +{ +public: +//------------------------------------------------------------------------ + using ClassInfos = std::vector; + using PluginFactoryPtr = Steinberg::IPtr; + +//------------------------------------------------------------------------ + explicit PluginFactory (const PluginFactoryPtr& factory) noexcept; + + void setHostContext (Steinberg::FUnknown* context) const noexcept; + + FactoryInfo info () const noexcept; + uint32_t classCount () const noexcept; + ClassInfos classInfos () const noexcept; + + template + Steinberg::IPtr createInstance (const UID& classID) const noexcept; + + const PluginFactoryPtr& get () const noexcept { return factory; } +//------------------------------------------------------------------------ +private: + PluginFactoryPtr factory; +}; + +//------------------------------------------------------------------------ +class Module +{ +public: +//------------------------------------------------------------------------ + using Ptr = std::shared_ptr; + using PathList = std::vector; + +//------------------------------------------------------------------------ + static Ptr create (const std::string& path, std::string& errorDescription); + static PathList getModulePaths (); + + const std::string& getName () const noexcept { return name; } + const std::string& getPath () const noexcept { return path; } + const PluginFactory& getFactory () const noexcept { return factory; } +//------------------------------------------------------------------------ +protected: + virtual ~Module () noexcept = default; + virtual bool load (const std::string& path, std::string& errorDescription) = 0; + + PluginFactory factory {nullptr}; + std::string name; + std::string path; +}; + +//------------------------------------------------------------------------ +template +inline Steinberg::IPtr PluginFactory::createInstance (const UID& classID) const noexcept +{ + T* obj = nullptr; + if (factory->createInstance (classID.data (), T::iid, reinterpret_cast (&obj)) == + Steinberg::kResultTrue) + return Steinberg::owned (obj); + return nullptr; +} + +//------------------------------------------------------------------------ +} // Hosting +} // VST3 diff --git a/source/includes/vst3sdk/public.sdk/source/vst/hosting/module_linux.cpp b/source/includes/vst3sdk/public.sdk/source/vst/hosting/module_linux.cpp new file mode 100644 index 000000000..a365b7449 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/hosting/module_linux.cpp @@ -0,0 +1,277 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : +// Filename : public.sdk/source/vst/hosting/module_linux.cpp +// Created by : Steinberg, 08/2016 +// Description : hosting module classes (linux implementation) +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "stringconvert.h" +#include "module.h" +#include "optional.h" +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------ +extern "C" { +typedef bool (PLUGIN_API* ModuleEntryFunc) (void*); +typedef bool (PLUGIN_API* ModuleExitFunc) (); +} + +//------------------------------------------------------------------------ +namespace VST3 { +namespace Hosting { + +//------------------------------------------------------------------------ +namespace { + +//------------------------------------------------------------------------ +Optional getCurrentMachineName () +{ + struct utsname unameData; + + int res = uname (&unameData); + if (res != 0) + return {}; + + return {unameData.machine}; +} + +using Path = std::experimental::filesystem::path; +//------------------------------------------------------------------------ +Optional getApplicationPath () +{ + std::string appPath = ""; + + pid_t pid = getpid (); + char buf[10]; + sprintf (buf, "%d", pid); + std::string _link = "/proc/"; + _link.append (buf); + _link.append ("/exe"); + char proc[1024]; + int ch = readlink (_link.c_str (), proc, 1024); + if (ch == -1) + return {}; + + proc[ch] = 0; + appPath = proc; + std::string::size_type t = appPath.find_last_of ("/"); + appPath = appPath.substr (0, t); + + return Path {appPath}; +} + +//------------------------------------------------------------------------ +class LinuxModule : public Module +{ +public: + template + T getFunctionPointer (const char* name) + { + return reinterpret_cast (dlsym (module, name)); + } + + ~LinuxModule () + { + factory = PluginFactory (nullptr); + + if (module) + { + if (auto moduleExit = getFunctionPointer ("ModuleExit")) + moduleExit (); + + dlclose (module); + } + } + + static Optional getSOPath (const std::string& inPath) + { + using namespace std::experimental; + + Path modulePath {inPath}; + if (!filesystem::is_directory (modulePath)) + return {}; + + auto stem = modulePath.stem (); + + modulePath /= "Contents"; + if (!filesystem::is_directory (modulePath)) + return {}; + + auto machine = getCurrentMachineName (); + if (!machine) + return {}; + + modulePath /= *machine + "-linux"; + if (!filesystem::is_directory (modulePath)) + return {}; + + stem.replace_extension (".so"); + modulePath /= stem; + return Optional (std::move (modulePath)); + } + + bool load (const std::string& inPath, std::string& errorDescription) override + { + auto modulePath = getSOPath (inPath); + if (!modulePath) + { + errorDescription = inPath + " is not a module directory."; + return false; + } + + module = dlopen (modulePath->generic_u8string ().data (), RTLD_LAZY); + if (!module) + { + errorDescription = "dlopen failed.\n"; + errorDescription += dlerror (); + return false; + } + + auto factoryProc = getFunctionPointer ("GetPluginFactory"); + if (!factoryProc) + { + errorDescription = + "The shared library does not export the required 'GetPluginFactory' function"; + return false; + } + auto moduleEntry = getFunctionPointer ("ModuleEntry"); + if (!moduleEntry) + { + errorDescription = + "The shared library does not export the required 'ModuleEntry' function"; + return false; + } + if (!moduleEntry (module)) + { + errorDescription = "Calling 'ModuleEntry' failed"; + return false; + } + auto f = Steinberg::FUnknownPtr (factoryProc ()); + if (!f) + { + errorDescription = "Calling 'GetPluginFactory' returned nullptr"; + return false; + } + factory = PluginFactory (f); + return true; + } + + void* module {nullptr}; +}; + +//------------------------------------------------------------------------ +void findFilesWithExt (const std::string& path, const std::string& ext, Module::PathList& pathList) +{ + try + { + using namespace std::experimental; + for (auto& p : filesystem::recursive_directory_iterator (path)) + { + if (p.path ().extension () == ext && filesystem::is_directory (p)) + { + pathList.push_back (p.path ().generic_u8string ()); + } + } + } + catch (...) + { + } +} + +//------------------------------------------------------------------------ +void findModules (const std::string& path, Module::PathList& pathList) +{ + findFilesWithExt (path, ".vst3", pathList); +} + +//------------------------------------------------------------------------ +} // anonymous + +//------------------------------------------------------------------------ +Module::Ptr Module::create (const std::string& path, std::string& errorDescription) +{ + auto module = std::make_shared (); + if (module->load (path, errorDescription)) + { + auto it = std::find_if (path.rbegin (), path.rend (), + [] (const std::string::value_type& c) { return c == '/'; }); + if (it != path.rend ()) + module->name = {it.base (), path.end ()}; + return module; + } + return nullptr; +} + +//------------------------------------------------------------------------ +Module::PathList Module::getModulePaths () +{ + /* VST3 component locations on linux : + * User privately installed : $HOME/.vst3/ + * Distribution installed : /usr/lib/vst3/ + * Locally installed : /usr/local/lib/vst3/ + * Application : /$APPFOLDER/vst3/ + */ + + const auto systemPaths = {"/usr/lib/vst3/", "/usr/local/lib/vst3/"}; + + // 32bit Plug-ins on 64bit OS + // const auto systemPaths = {"/usr/lib32/vst3/", "/usr/local/lib32/vst3/"}; + + PathList list; + if (auto homeDir = getenv ("HOME")) + { + std::experimental::filesystem::path homePath (homeDir); + homePath /= ".vst3"; + findModules (homePath.generic_u8string (), list); + } + for (auto path : systemPaths) + findModules (path, list); + + // application level + auto appPath = getApplicationPath (); + if (appPath) + { + *appPath /= "vst3"; + findModules (appPath->generic_u8string (), list); + } + + return list; +} + +//------------------------------------------------------------------------ +} // Hosting +} // VST3 diff --git a/source/includes/vst3sdk/public.sdk/source/vst/hosting/module_mac.mm b/source/includes/vst3sdk/public.sdk/source/vst/hosting/module_mac.mm new file mode 100644 index 000000000..3c4d0b643 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/hosting/module_mac.mm @@ -0,0 +1,299 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/hosting/module_mac.mm +// Created by : Steinberg, 08/2016 +// Description : hosting module classes (macOS implementation) +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#import "module.h" + +#import +#import + +//------------------------------------------------------------------------ +extern "C" { +typedef bool (*BundleEntryFunc) (CFBundleRef); +typedef bool (*BundleExitFunc) (); +} + +//------------------------------------------------------------------------ +namespace VST3 { +namespace Hosting { + +//------------------------------------------------------------------------ +namespace { + +//------------------------------------------------------------------------ +template +class CFPtr +{ +public: + inline CFPtr (const T& obj = nullptr) : obj (obj) {} + inline CFPtr (CFPtr&& other) { *this = other; } + inline ~CFPtr () + { + if (obj) + CFRelease (obj); + } + + inline CFPtr& operator= (CFPtr&& other) + { + obj = other.obj; + other.obj = nullptr; + return *this; + } + inline CFPtr& operator= (const T& o) + { + if (obj) + CFRelease (obj); + obj = o; + return *this; + } + inline operator T () const { return obj; } // act as T +private: + CFPtr (const CFPtr& other) = delete; + CFPtr& operator= (const CFPtr& other) = delete; + + T obj = nullptr; +}; + +//------------------------------------------------------------------------ +class MacModule : public Module +{ +public: + template + T getFunctionPointer (const char* name) + { + assert (bundle); + CFPtr functionName { + CFStringCreateWithCString (kCFAllocatorDefault, name, kCFStringEncodingASCII)}; + return reinterpret_cast (CFBundleGetFunctionPointerForName (bundle, functionName)); + } + + bool loadInternal (const std::string& path, std::string& errorDescription) + { + CFPtr url (CFURLCreateFromFileSystemRepresentation ( + kCFAllocatorDefault, reinterpret_cast (path.data ()), path.length (), + true)); + if (!url) + return false; + bundle = CFBundleCreate (kCFAllocatorDefault, url); + CFErrorRef error = nullptr; + if (!bundle || !CFBundleLoadExecutableAndReturnError (bundle, &error)) + { + if (error) + { + CFPtr errorString (CFErrorCopyDescription (error)); + if (errorString) + { + auto stringLength = CFStringGetLength (errorString); + auto maxSize = + CFStringGetMaximumSizeForEncoding (stringLength, kCFStringEncodingUTF8); + auto buffer = std::make_unique (maxSize); + if (CFStringGetCString (errorString, buffer.get (), maxSize, + kCFStringEncodingUTF8)) + errorDescription = buffer.get (); + CFRelease (error); + } + } + else + { + errorDescription = "Could not create Bundle for path: " + path; + } + return false; + } + auto bundleEntry = getFunctionPointer ("bundleEntry"); + if (!bundleEntry) + { + errorDescription = "Bundle does not export the required 'bundleEntry' function"; + return false; + } + auto bundleExit = getFunctionPointer ("bundleExit"); + if (!bundleExit) + { + errorDescription = "Bundle does not export the required 'bundleExit' function"; + return false; + } + auto factoryProc = getFunctionPointer ("GetPluginFactory"); + if (!factoryProc) + { + errorDescription = "Bundle does not export the required 'GetPluginFactory' function"; + return false; + } + if (!bundleEntry (bundle)) + { + errorDescription = "Calling 'bundleEntry' failed"; + return false; + } + auto f = Steinberg::FUnknownPtr (factoryProc ()); + if (!f) + { + errorDescription = "Calling 'GetPluginFactory' returned nullptr"; + return false; + } + factory = PluginFactory (f); + return true; + } + + bool load (const std::string& path, std::string& errorDescription) override + { + if (!path.empty () && path[0] != '/') + { + auto buffer = std::make_unique (PATH_MAX); + auto workDir = getcwd (buffer.get (), PATH_MAX); + if (workDir) + { + std::string wd (workDir); + wd += "/"; + return loadInternal (wd + path, errorDescription); + } + } + return loadInternal (path, errorDescription); + } + + ~MacModule () + { + factory = PluginFactory (nullptr); + + if (bundle) + { + if (auto bundleExit = getFunctionPointer ("bundleExit")) + bundleExit (); + + if (CFBundleIsExecutableLoaded ((CFBundleRef)bundle)) + { + CFBundleUnloadExecutable ((CFBundleRef)bundle); + } + } + + } + + CFPtr bundle; +}; + +//------------------------------------------------------------------------ +void getModules (NSSearchPathDomainMask domain, Module::PathList& result) +{ + NSURL* libraryUrl = [[NSFileManager defaultManager] URLForDirectory:NSLibraryDirectory + inDomain:domain + appropriateForURL:nil + create:NO + error:nil]; + if (libraryUrl == nil) + return; + NSURL* audioUrl = [libraryUrl URLByAppendingPathComponent:@"Audio"]; + if (audioUrl == nil) + return; + NSURL* plugInsUrl = [audioUrl URLByAppendingPathComponent:@"Plug-Ins"]; + if (plugInsUrl == nil) + return; + NSURL* vst3Url = + [[plugInsUrl URLByAppendingPathComponent:@"VST3"] URLByResolvingSymlinksInPath]; + if (vst3Url == nil) + return; + NSDirectoryEnumerator* enumerator = [[NSFileManager defaultManager] + enumeratorAtURL:[vst3Url URLByResolvingSymlinksInPath] + includingPropertiesForKeys:nil + options:NSDirectoryEnumerationSkipsPackageDescendants + errorHandler:nil]; + for (NSURL* url in enumerator) + { + if ([[[url lastPathComponent] pathExtension] isEqualToString:@"vst3"]) + { + + result.emplace_back ([url.path UTF8String]); + } + } +} + +//------------------------------------------------------------------------ +void getApplicationModules (Module::PathList& result) +{ + auto bundle = CFBundleGetMainBundle (); + if (!bundle) + return; + auto bundleUrl = static_cast (CFBundleCopyBundleURL (bundle)); + if (!bundleUrl) + return; + auto resUrl = [bundleUrl URLByAppendingPathComponent:@"Contents"]; + if (!resUrl) + return; + auto vst3Url = [resUrl URLByAppendingPathComponent:@"VST3"]; + if (!vst3Url) + return; + NSDirectoryEnumerator* enumerator = [[NSFileManager defaultManager] + enumeratorAtURL:[vst3Url URLByResolvingSymlinksInPath] + includingPropertiesForKeys:nil + options:NSDirectoryEnumerationSkipsPackageDescendants + errorHandler:nil]; + for (NSURL* url in enumerator) + { + if ([[[url lastPathComponent] pathExtension] isEqualToString:@"vst3"]) + { + result.emplace_back ([url.path UTF8String]); + } + } +} + +//------------------------------------------------------------------------ +} // anonymous + +//------------------------------------------------------------------------ +Module::Ptr Module::create (const std::string& path, std::string& errorDescription) +{ + auto module = std::make_shared (); + if (module->load (path, errorDescription)) + { + module->path = path; + auto it = std::find_if (path.rbegin (), path.rend (), + [] (const std::string::value_type& c) { return c == '/'; }); + if (it != path.rend ()) + module->name = {it.base (), path.end ()}; + return module; + } + return nullptr; +} + +//------------------------------------------------------------------------ +Module::PathList Module::getModulePaths () +{ + PathList list; + getModules (NSUserDomainMask, list); + getModules (NSLocalDomainMask, list); + // TODO getModules (NSNetworkDomainMask, list); + getApplicationModules (list); + return list; +} + +//------------------------------------------------------------------------ +} // Hosting +} // VST3 diff --git a/source/includes/vst3sdk/public.sdk/source/vst/hosting/module_win32.cpp b/source/includes/vst3sdk/public.sdk/source/vst/hosting/module_win32.cpp new file mode 100644 index 000000000..c2a05ca85 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/hosting/module_win32.cpp @@ -0,0 +1,192 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : +// Filename : public.sdk/source/vst/hosting/module_win32.cpp +// Created by : Steinberg, 08/2016 +// Description : hosting module classes (win32 implementation) +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "stringconvert.h" +#include "module.h" +#include +#include +#include +#include + +#pragma comment(lib, "Shell32") + +//------------------------------------------------------------------------ +extern "C" { +typedef bool (PLUGIN_API* InitModuleFunc) (); +typedef bool (PLUGIN_API* ExitModuleFunc) (); +} + +using namespace std::experimental; + +//------------------------------------------------------------------------ +namespace VST3 { +namespace Hosting { + +//------------------------------------------------------------------------ +namespace { + +//------------------------------------------------------------------------ +class Win32Module : public Module +{ +public: + template + T getFunctionPointer (const char* name) + { + return reinterpret_cast (GetProcAddress (module, name)); + } + + ~Win32Module () + { + factory = PluginFactory (nullptr); + + if (module) + { + if (auto dllExit = getFunctionPointer ("ExitDll")) + dllExit (); + + FreeLibrary ((HMODULE)module); + } + } + + bool load (const std::string& inPath, std::string& errorDescription) override + { + auto wideStr = StringConvert::convert (inPath); + module = LoadLibraryW (reinterpret_cast (wideStr.data ())); + if (!module) + { + // TODO: is there an API to get more information about the failure ? + errorDescription = "LoadLibray failed."; + return false; + } + + auto dllEntry = getFunctionPointer ("InitDll"); + auto factoryProc = getFunctionPointer ("GetPluginFactory"); + if (!factoryProc) + { + errorDescription = "dll does not export the required 'GetPluginFactory' function"; + return false; + } + if (dllEntry && !dllEntry ()) + { + errorDescription = "Calling 'InitDll' failed"; + return false; + } + auto f = Steinberg::FUnknownPtr (factoryProc ()); + if (!f) + { + errorDescription = "Calling 'GetPluginFactory' returned nullptr"; + return false; + } + factory = PluginFactory (f); + return true; + } + + HINSTANCE module {nullptr}; +}; + +//------------------------------------------------------------------------ +Optional getKnownFolder (REFKNOWNFOLDERID folderID) +{ + PWSTR wideStr {}; + if (FAILED (SHGetKnownFolderPath (folderID, 0, nullptr, &wideStr))) + return {}; + return StringConvert::convert (wideStr); +} + +//------------------------------------------------------------------------ +void findFilesWithExt (const std::string& path, const std::string& ext, Module::PathList& pathList) +{ + for (auto& p : filesystem::recursive_directory_iterator (path)) + { + if (p.path ().extension () == ext) + { + pathList.push_back (p.path ().generic_u8string ()); + } + } +} + +//------------------------------------------------------------------------ +void findModules (const std::string& path, Module::PathList& pathList) +{ + findFilesWithExt (path, ".vst3", pathList); +} + +//------------------------------------------------------------------------ +} // anonymous + +//------------------------------------------------------------------------ +Module::Ptr Module::create (const std::string& path, std::string& errorDescription) +{ + auto module = std::make_shared (); + if (module->load (path, errorDescription)) + { + module->path = path; + auto it = std::find_if (path.rbegin (), path.rend (), + [] (const std::string::value_type& c) { return c == '/'; }); + if (it != path.rend ()) + module->name = {it.base (), path.end ()}; + return module; + } + return nullptr; +} + +//------------------------------------------------------------------------ +Module::PathList Module::getModulePaths () +{ + // find plug-ins located in common/VST3 + PathList list; + if (auto knownFolder = getKnownFolder (FOLDERID_ProgramFilesCommon)) + { + filesystem::path p (*knownFolder); + p.append ("VST3"); + findModules (p.generic_u8string (), list); + } + + // find plug-ins located in VST3 (application folder) + WCHAR modulePath[MAX_PATH + 1]; + GetModuleFileNameW (nullptr, modulePath, MAX_PATH); + auto appPath = StringConvert::convert (modulePath); + filesystem::path path (appPath); + path = path.parent_path (); + path = path.append ("VST3"); + findModules (path.generic_u8string (), list); + + return list; +} + +//------------------------------------------------------------------------ +} // Hosting +} // VST3 diff --git a/source/includes/vst3sdk/public.sdk/source/vst/hosting/optional.h b/source/includes/vst3sdk/public.sdk/source/vst/hosting/optional.h new file mode 100644 index 000000000..c568bbbc0 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/hosting/optional.h @@ -0,0 +1,123 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/hosting/optional.h +// Created by : Steinberg, 08/2016 +// Description : optional helper +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include +#include +#include + +//------------------------------------------------------------------------ +namespace VST3 { + +//------------------------------------------------------------------------ +template +struct Optional +{ + Optional () noexcept : valid (false) {} + explicit Optional (const T& v) noexcept : value (v), valid (true) {} + Optional (T&& v) noexcept : value (std::move (v)), valid (true) {} + + Optional (Optional&& other) noexcept { *this = std::move (other); } + Optional& operator= (Optional&& other) noexcept + { + valid = other.valid; + value = std::move (other.value); + return *this; + } + + explicit operator bool () const noexcept + { + setValidationChecked (); + return valid; + } + + const T& operator* () const noexcept + { + checkValid (); + return value; + } + + const T* operator-> () const noexcept + { + checkValid (); + return &value; + } + + T& operator* () noexcept + { + checkValid (); + return value; + } + + T* operator-> () noexcept + { + checkValid (); + return &value; + } + + void swap (T& other) noexcept + { + checkValid (); + auto tmp = std::move (other); + other = std::move (value); + value = std::move (tmp); + } + +private: + T value {}; + bool valid; + +#if !defined(NDEBUG) + mutable bool validationChecked {false}; +#endif + + void setValidationChecked () const + { +#if !defined(NDEBUG) + validationChecked = true; +#endif + } + void checkValid () const + { +#if !defined(NDEBUG) + assert (validationChecked); +#endif + } +}; + +//------------------------------------------------------------------------ +} diff --git a/source/includes/vst3sdk/public.sdk/source/vst/hosting/parameterchanges.cpp b/source/includes/vst3sdk/public.sdk/source/vst/hosting/parameterchanges.cpp new file mode 100644 index 000000000..8047831ec --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/hosting/parameterchanges.cpp @@ -0,0 +1,323 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/hosting/parameterchanges.cpp +// Created by : Steinberg, 03/05/2008. +// Description : VST 3 parameter changes implementation +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "parameterchanges.h" + +namespace Steinberg { +namespace Vst { + +//----------------------------------------------------------------------------- +IMPLEMENT_FUNKNOWN_METHODS (ParameterChanges, IParameterChanges, IParameterChanges::iid) +IMPLEMENT_FUNKNOWN_METHODS (ParameterValueQueue, IParamValueQueue, IParamValueQueue::iid) + + +const int32 kQueueReservedPoints = 5; + +//----------------------------------------------------------------------------- +ParameterValueQueue::ParameterValueQueue (ParamID paramID) +: paramID (paramID) +{ + values.reserve (kQueueReservedPoints); + FUNKNOWN_CTOR +} + +//----------------------------------------------------------------------------- +ParameterValueQueue::~ParameterValueQueue () +{ + FUNKNOWN_DTOR +} + +//----------------------------------------------------------------------------- +void ParameterValueQueue::clear () +{ + values.clear (); +} + +//----------------------------------------------------------------------------- +int32 PLUGIN_API ParameterValueQueue::getPointCount () +{ + return (int32)values.size (); +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API ParameterValueQueue::getPoint (int32 index, int32& sampleOffset, ParamValue& value) +{ + if (index < (int32)values.size ()) + { + const ParameterQueueValue& queueValue = values[index]; + sampleOffset = queueValue.sampleOffset; + value = queueValue.value; + return kResultTrue; + } + return kResultFalse; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API ParameterValueQueue::addPoint (int32 sampleOffset, ParamValue value, int32& index) +{ + int32 destIndex = (int32)values.size (); + for (uint32 i = 0; i < values.size (); i++) + { + if (values[i].sampleOffset == sampleOffset) + { + values[i].value = value; + index = i; + return kResultTrue; + } + else if (values[i].sampleOffset > sampleOffset) + { + destIndex = i; + break; + } + } + + // need new point + ParameterQueueValue queueValue (value, sampleOffset); + if (destIndex == (int32)values.size ()) + values.push_back (queueValue); + else + values.insert (values.begin () + destIndex, queueValue); + + index = destIndex; + + return kResultTrue; +} + +//----------------------------------------------------------------------------- +// ParameterChanges +//----------------------------------------------------------------------------- +ParameterChanges::ParameterChanges (int32 maxParameters) +: usedQueueCount (0) +{ + FUNKNOWN_CTOR + setMaxParameters (maxParameters); +} + +//----------------------------------------------------------------------------- +ParameterChanges::~ParameterChanges () +{ + setMaxParameters (0); + + FUNKNOWN_DTOR +} + +//----------------------------------------------------------------------------- +void ParameterChanges::setMaxParameters (int32 maxParameters) +{ + if (maxParameters < 0) + return; + + while ((int32)queues.size () < maxParameters) + { + ParameterValueQueue* valueQueue = new ParameterValueQueue (0xffffffff); + queues.push_back (valueQueue); + } + + while ((int32)queues.size () > maxParameters) + { + queues.back ()->release (); + queues.pop_back (); + } + if (usedQueueCount > maxParameters) + usedQueueCount = maxParameters; +} + +//----------------------------------------------------------------------------- +void ParameterChanges::clearQueue () +{ + usedQueueCount = 0; +} + +//----------------------------------------------------------------------------- +int32 PLUGIN_API ParameterChanges::getParameterCount () +{ + return usedQueueCount; +} + +//----------------------------------------------------------------------------- +IParamValueQueue* PLUGIN_API ParameterChanges::getParameterData (int32 index) +{ + if (index < usedQueueCount) + return queues[index]; + return nullptr; +} + +//----------------------------------------------------------------------------- +IParamValueQueue* PLUGIN_API ParameterChanges::addParameterData (const ParamID& pid, int32& index) +{ + for (int32 i = 0; i < usedQueueCount; i++) + { + if (queues[i]->getParameterId () == pid) + { + index = i; + return queues[i]; + } + } + + ParameterValueQueue* valueQueue = nullptr; + if (usedQueueCount < (int32)queues.size ()) + { + valueQueue = queues[usedQueueCount]; + valueQueue->setParamID (pid); + valueQueue->clear (); + } + else + { + valueQueue = new ParameterValueQueue (pid); + queues.push_back (valueQueue); + } + + index = usedQueueCount; + usedQueueCount++; + return valueQueue; +} + + +//----------------------------------------------------------------------------- +// ParameterChangeTransfer +//----------------------------------------------------------------------------- +ParameterChangeTransfer::ParameterChangeTransfer (int32 maxParameters) +: size (0) +, changes (nullptr) +, readIndex (0) +, writeIndex (0) +{ + setMaxParameters (maxParameters); +} + +//----------------------------------------------------------------------------- +ParameterChangeTransfer::~ParameterChangeTransfer () +{ + setMaxParameters (0); +} + +//----------------------------------------------------------------------------- +void ParameterChangeTransfer::setMaxParameters (int32 maxParameters) +{ + // reserve memory for twice the amount of all parameters + int32 newSize = maxParameters * 2; + if (size != newSize) + { + if (changes) + delete [] changes; + changes = nullptr; + size = newSize; + if (size > 0) + changes = new ParameterChange [size]; + } +} + +//----------------------------------------------------------------------------- +void ParameterChangeTransfer::addChange (ParamID pid, ParamValue value, int32 sampleOffset) +{ + if (changes) + { + changes[writeIndex].id = pid; + changes[writeIndex].value = value; + changes[writeIndex].sampleOffset = sampleOffset; + + int32 newWriteIndex = writeIndex + 1; + if (newWriteIndex >= size) + newWriteIndex = 0; + if (readIndex != newWriteIndex) + writeIndex = newWriteIndex; + } +} + +//----------------------------------------------------------------------------- +bool ParameterChangeTransfer::getNextChange (ParamID& pid, ParamValue& value, int32& sampleOffset) +{ + if (!changes) + return false; + + int32 currentWriteIndex = writeIndex; + if (readIndex != currentWriteIndex) + { + pid = changes [readIndex].id; + value = changes [readIndex].value; + sampleOffset = changes [readIndex].sampleOffset; + + int32 newReadIndex = readIndex + 1; + if (newReadIndex >= size) + newReadIndex = 0; + readIndex = newReadIndex; + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +void ParameterChangeTransfer::transferChangesTo (ParameterChanges& dest) +{ + ParamID pid; + ParamValue value; + int32 sampleOffset; + int32 index; + + while (getNextChange (pid, value, sampleOffset)) + { + IParamValueQueue* queue = dest.addParameterData (pid, index); + if (queue) + { + queue->addPoint (sampleOffset, value, index); + } + } +} + +//----------------------------------------------------------------------------- +void ParameterChangeTransfer::transferChangesFrom (ParameterChanges& source) +{ + ParamValue value; + int32 sampleOffset; + for (int32 i = 0; i < source.getParameterCount (); i++) + { + IParamValueQueue* queue = source.getParameterData (i); + if (queue) + { + for (int32 j = 0; j < queue->getPointCount (); j++) + { + if (queue->getPoint (j, sampleOffset, value) == kResultTrue) + { + addChange (queue->getParameterId (), value, sampleOffset); + } + } + } + } +} + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/hosting/parameterchanges.h b/source/includes/vst3sdk/public.sdk/source/vst/hosting/parameterchanges.h new file mode 100644 index 000000000..03836f451 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/hosting/parameterchanges.h @@ -0,0 +1,142 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/hosting/parameterchanges.h +// Created by : Steinberg, 03/05/2008. +// Description : VST 3 parameter changes implementation +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "pluginterfaces/vst/ivstparameterchanges.h" +#include + +namespace Steinberg { +namespace Vst { + +//------------------------------------------------------------------------ +/** Implementation's example of IParamValueQueue - not threadsave!. +\ingroup hostingBase +*/ +class ParameterValueQueue : public IParamValueQueue +{ +public: + //------------------------------------------------------------------------ + ParameterValueQueue (ParamID paramID); + virtual ~ParameterValueQueue (); + + ParamID PLUGIN_API getParameterId () SMTG_OVERRIDE { return paramID; } + int32 PLUGIN_API getPointCount () SMTG_OVERRIDE; + tresult PLUGIN_API getPoint (int32 index, int32& sampleOffset, ParamValue& value) SMTG_OVERRIDE; + tresult PLUGIN_API addPoint (int32 sampleOffset, ParamValue value, int32& index) SMTG_OVERRIDE; + + void setParamID (ParamID pID) {paramID = pID;} + void clear (); + //------------------------------------------------------------------------ + DECLARE_FUNKNOWN_METHODS +protected: + ParamID paramID; + + struct ParameterQueueValue + { + ParameterQueueValue (ParamValue value, int32 sampleOffset) : value (value), sampleOffset (sampleOffset) {} + ParamValue value; + int32 sampleOffset; + }; + std::vector values; +}; + +//------------------------------------------------------------------------ +/** Implementation's example of IParameterChanges - not threadsave!. +\ingroup hostingBase +*/ +class ParameterChanges : public IParameterChanges +{ +public: + //------------------------------------------------------------------------ + ParameterChanges (int32 maxParameters = 0); + virtual ~ParameterChanges (); + + void clearQueue (); + void setMaxParameters (int32 maxParameters); + + //---IParameterChanges----------------------------- + int32 PLUGIN_API getParameterCount () SMTG_OVERRIDE; + IParamValueQueue* PLUGIN_API getParameterData (int32 index) SMTG_OVERRIDE; + IParamValueQueue* PLUGIN_API addParameterData (const ParamID& pid, int32& index) SMTG_OVERRIDE; + + //------------------------------------------------------------------------ + DECLARE_FUNKNOWN_METHODS +protected: + std::vector queues; + int32 usedQueueCount; +}; + + +//------------------------------------------------------------------------ +/** Ring buffer for transferring parameter changes from a writer to a read thread . +\ingroup hostingBase +*/ +class ParameterChangeTransfer +{ +public: + //------------------------------------------------------------------------ + ParameterChangeTransfer (int32 maxParameters = 0); + virtual ~ParameterChangeTransfer (); + + void setMaxParameters (int32 maxParameters); + + void addChange (ParamID pid, ParamValue value, int32 sampleOffset); + bool getNextChange (ParamID& pid, ParamValue& value, int32& sampleOffset); + + void transferChangesTo (ParameterChanges& dest); + void transferChangesFrom (ParameterChanges& source); + + void removeChanges () { writeIndex = readIndex; } + + //------------------------------------------------------------------------ +protected: + struct ParameterChange + { + ParamID id; + ParamValue value; + int32 sampleOffset; + }; + int32 size; + ParameterChange* changes; + + volatile int32 readIndex; + volatile int32 writeIndex; +}; + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/hosting/plugprovider.cpp b/source/includes/vst3sdk/public.sdk/source/vst/hosting/plugprovider.cpp new file mode 100644 index 000000000..864226ce9 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/hosting/plugprovider.cpp @@ -0,0 +1,320 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : plugprovider.cpp +// Created by : Steinberg, 08/2016 +// Description : VST 3 Plug-in Provider class +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "plugprovider.h" + +#include "pluginterfaces/vst/ivstcomponent.h" +#include "pluginterfaces/vst/ivsteditcontroller.h" +#include "pluginterfaces/vst/ivstmessage.h" + +namespace Steinberg { +extern FUnknown* gStandardPluginContext; + +FUnknown* getPluginContext () +{ + return gStandardPluginContext; +} +} + +#include +#include + +static std::ostream* errorStream = &std::cout; + +namespace Steinberg { +namespace Vst { + +//------------------------------------------------------------------------ +class ConnectionProxy : public FObject, public IConnectionPoint +{ +public: + ConnectionProxy (IConnectionPoint* srcConnection); + virtual ~ConnectionProxy (); + + //--- from IConnectionPoint + tresult PLUGIN_API connect (IConnectionPoint* other) override; + tresult PLUGIN_API disconnect (IConnectionPoint* other) override; + tresult PLUGIN_API notify (IMessage* message) override; + + bool disconnect (); + + OBJ_METHODS (ConnectionProxy, FObject) + REFCOUNT_METHODS (FObject) + DEF_INTERFACES_1 (IConnectionPoint, FObject) + +protected: + IPtr srcConnection; + IPtr dstConnection; +}; + +//------------------------------------------------------------------------ +ConnectionProxy::ConnectionProxy (IConnectionPoint* srcConnection) +: srcConnection (srcConnection) // share it +{ +} + +//------------------------------------------------------------------------ +ConnectionProxy::~ConnectionProxy () +{ +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API ConnectionProxy::connect (IConnectionPoint* other) +{ + if (other == nullptr) + return kInvalidArgument; + if (dstConnection) + return kResultFalse; + + dstConnection = other; // share it + tresult res = srcConnection->connect (this); + if (res != kResultTrue) + dstConnection = nullptr; + return res; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API ConnectionProxy::disconnect (IConnectionPoint* other) +{ + if (!other) + return kInvalidArgument; + + if (other == dstConnection) + { + if (srcConnection) + srcConnection->disconnect (this); + dstConnection = nullptr; + return kResultTrue; + } + + return kInvalidArgument; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API ConnectionProxy::notify (IMessage* message) +{ + if (dstConnection) + { + // TODO we should test if we are in UI main thread else postpone the message + return dstConnection->notify (message); + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +bool ConnectionProxy::disconnect () +{ + return disconnect (dstConnection) == kResultTrue; +} + +//------------------------------------------------------------------------ +// PlugProvider +//------------------------------------------------------------------------ +PlugProvider::PlugProvider (const PluginFactory& factory, ClassInfo classInfo, bool plugIsGlobal) +: factory (factory) +, component (nullptr) +, controller (nullptr) +, classInfo (classInfo) +, plugIsGlobal (plugIsGlobal) +{ + if (plugIsGlobal) + { + setupPlugin (getPluginContext ()); + } +} + +//------------------------------------------------------------------------ +PlugProvider::~PlugProvider () +{ + terminatePlugin (); +} + +//------------------------------------------------------------------------ +IComponent* PlugProvider::getComponent () +{ + if (!component) + setupPlugin (getPluginContext ()); + + if (component) + component->addRef (); + + return component; +} + +//------------------------------------------------------------------------ +IEditController* PlugProvider::getController () +{ + if (controller) + controller->addRef (); + + // 'iController == 0' is allowed! In this case the plug has no controller + return controller; +} + +//------------------------------------------------------------------------ +tresult PlugProvider::getPluginUID (FUID& uid) const +{ + uid = classInfo.ID ().data (); + return kResultOk; +} + +//------------------------------------------------------------------------ +tresult PlugProvider::releasePlugIn (IComponent* iComponent, IEditController* iController) +{ + if (iComponent) + iComponent->release (); + + if (iController) + iController->release (); + + if (!plugIsGlobal) + { + terminatePlugin (); + } + + return kResultOk; +} + +//------------------------------------------------------------------------ +bool PlugProvider::setupPlugin (FUnknown* hostContext) +{ + bool res = false; + + //---create Plug-in here!-------------- + // create its component part + component = factory.createInstance (classInfo.ID ()); + if (component) + { + // initialize the component with our context + res = (component->initialize (hostContext) == kResultOk); + + // try to create the controller part from the component + // (for Plug-ins which did not succeed to separate component from controller) + if (component->queryInterface (IEditController::iid, (void**)&controller) != kResultTrue) + { + TUID controllerCID; + + // ask for the associated controller class ID + if (component->getControllerClassId (controllerCID) == kResultTrue) + { + // create its controller part created from the factory + controller = factory.createInstance (VST3::UID (controllerCID)); + if (controller) + { + // initialize the component with our context + res = (controller->initialize (hostContext) == kResultOk); + } + } + } + } + else if (errorStream) + { + *errorStream << "Failed to create instance of " << classInfo.name () << "!\n"; + } + + if (res) + connectComponents (); + + return res; +} + +//------------------------------------------------------------------------ +bool PlugProvider::connectComponents () +{ + if (!component || !controller) + return false; + + FUnknownPtr compICP (component); + FUnknownPtr contrICP (controller); + if (!compICP || !contrICP) + return false; + + bool res = false; + + componentCP = NEW ConnectionProxy (compICP); + controllerCP = NEW ConnectionProxy (contrICP); + + if (componentCP->connect (contrICP) != kResultTrue) + { + // TODO: Alert or what for non conformant plugin ? + } + else + { + if (controllerCP->connect (compICP) != kResultTrue) + { + // TODO: Alert or what for non conformant plugin ? + } + else + res = true; + } + return res; +} + +//------------------------------------------------------------------------ +bool PlugProvider::disconnectComponents () +{ + if (!componentCP || !controllerCP) + return false; + + bool res = componentCP->disconnect (); + res &= controllerCP->disconnect (); + + componentCP = nullptr; + controllerCP = nullptr; + + return res; +} + +//------------------------------------------------------------------------ +void PlugProvider::terminatePlugin () +{ + disconnectComponents (); + + bool controllerIsComponent = false; + if (component) + { + controllerIsComponent = FUnknownPtr (component).getInterface () != 0; + component->terminate (); + } + + if (controller && controllerIsComponent == false) + controller->terminate (); + + component = nullptr; + controller = nullptr; +} +} +} // namespaces diff --git a/source/includes/vst3sdk/public.sdk/source/vst/hosting/plugprovider.h b/source/includes/vst3sdk/public.sdk/source/vst/hosting/plugprovider.h new file mode 100644 index 000000000..4fc789434 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/hosting/plugprovider.h @@ -0,0 +1,96 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : plugprovider.h +// Created by : Steinberg, 04/2005 +// Description : VST 3 Plug-in Provider class +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "base/source/fobject.h" +#include "public.sdk/source/vst/hosting/module.h" +#include "public.sdk/source/vst/testsuite/vsttestsuite.h" // for IPlugProvider + +namespace Steinberg { +namespace Vst { + +class IComponent; +class IEditController; +class ConnectionProxy; + +//------------------------------------------------------------------------ +/** Helper for creating and initializing component. +\ingroup Validator */ +//------------------------------------------------------------------------ +class PlugProvider : public FObject, public IPlugProvider +{ +public: + using ClassInfo = VST3::Hosting::ClassInfo; + using PluginFactory = VST3::Hosting::PluginFactory; + + //--- --------------------------------------------------------------------- + PlugProvider (const PluginFactory& factory, ClassInfo info, bool plugIsGlobal = true); + virtual ~PlugProvider (); + + //--- from IPlugProvider ------------------ + IComponent* getComponent () SMTG_OVERRIDE; + IEditController* getController () SMTG_OVERRIDE; + tresult releasePlugIn (IComponent* component, IEditController* controller) SMTG_OVERRIDE; + const char8* getSubCategories () const SMTG_OVERRIDE + { + return classInfo.subCategoriesString ().data (); + } + tresult getPluginUID (FUID& uid) const SMTG_OVERRIDE; + + //--- --------------------------------------------------------------------- + OBJ_METHODS (PlugProvider, FObject) + REFCOUNT_METHODS (FObject) + DEF_INTERFACES_1 (IPlugProvider, FObject) +//------------------------------------------------------------------------ +protected: + bool setupPlugin (FUnknown* hostContext); + bool connectComponents (); + bool disconnectComponents (); + void terminatePlugin (); + + PluginFactory factory; + IPtr component; + IPtr controller; + ClassInfo classInfo; + + OPtr componentCP; + OPtr controllerCP; + + bool plugIsGlobal; +}; +} +} // namespaces diff --git a/source/includes/vst3sdk/public.sdk/source/vst/hosting/processdata.cpp b/source/includes/vst3sdk/public.sdk/source/vst/hosting/processdata.cpp new file mode 100644 index 000000000..bf4244e87 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/hosting/processdata.cpp @@ -0,0 +1,223 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/hosting/processdata.cpp +// Created by : Steinberg, 10/2005 +// Description : VST Hosting Utilities +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "processdata.h" + +//------------------------------------------------------------------------ +namespace Steinberg { +namespace Vst { + +//------------------------------------------------------------------------ +// HostProcessData +//------------------------------------------------------------------------ +HostProcessData::~HostProcessData () +{ + unprepare (); +} + +//------------------------------------------------------------------------ +bool HostProcessData::prepare (IComponent& component, int32 bufferSamples, + int32 _symbolicSampleSize) +{ + if (checkIfReallocationNeeded (component, bufferSamples, _symbolicSampleSize)) + { + unprepare (); + + symbolicSampleSize = _symbolicSampleSize; + channelBufferOwner = bufferSamples > 0; + + numInputs = createBuffers (component, inputs, kInput, bufferSamples); + numOutputs = createBuffers (component, outputs, kOutput, bufferSamples); + } + else + { + // reset silence flags + for (int32 i = 0; i < numInputs; i++) + { + inputs[i].silenceFlags = 0; + } + for (int32 i = 0; i < numOutputs; i++) + { + outputs[i].silenceFlags = 0; + } + } + + return true; +} + +//------------------------------------------------------------------------ +void HostProcessData::unprepare () +{ + destroyBuffers (inputs, numInputs); + destroyBuffers (outputs, numOutputs); + + channelBufferOwner = false; +} + +//------------------------------------------------------------------------ +bool HostProcessData::checkIfReallocationNeeded (IComponent& component, int32 bufferSamples, + int32 _symbolicSampleSize) +{ + if (channelBufferOwner != (bufferSamples > 0)) + return true; + if (symbolicSampleSize != _symbolicSampleSize) + return true; + + int32 inBusCount = component.getBusCount (kAudio, kInput); + if (inBusCount != numInputs) + return true; + + int32 outBusCount = component.getBusCount (kAudio, kOutput); + if (outBusCount != numOutputs) + return true; + + for (int32 i = 0; i < inBusCount; i++) + { + BusInfo busInfo = {0}; + + if (component.getBusInfo (kAudio, kInput, i, busInfo) == kResultTrue) + { + if (inputs[i].numChannels != busInfo.channelCount) + return true; + } + } + for (int32 i = 0; i < outBusCount; i++) + { + BusInfo busInfo = {0}; + + if (component.getBusInfo (kAudio, kOutput, i, busInfo) == kResultTrue) + { + if (outputs[i].numChannels != busInfo.channelCount) + return true; + } + } + return false; +} + +//----------------------------------------------------------------------------- +int32 HostProcessData::createBuffers (IComponent& component, AudioBusBuffers*& buffers, + BusDirection dir, int32 bufferSamples) +{ + int32 busCount = component.getBusCount (kAudio, dir); + if (busCount > 0) + { + buffers = new AudioBusBuffers[busCount]; + + for (int32 i = 0; i < busCount; i++) + { + BusInfo busInfo = {0}; + + if (component.getBusInfo (kAudio, dir, i, busInfo) == kResultTrue) + { + buffers[i].numChannels = busInfo.channelCount; + + // allocate for each channel + if (busInfo.channelCount > 0) + { + if (symbolicSampleSize == kSample64) + buffers[i].channelBuffers64 = new Sample64*[busInfo.channelCount]; + else + buffers[i].channelBuffers32 = new Sample32*[busInfo.channelCount]; + + for (int32 j = 0; j < busInfo.channelCount; j++) + { + if (symbolicSampleSize == kSample64) + { + if (bufferSamples > 0) + buffers[i].channelBuffers64[j] = new Sample64[bufferSamples]; + else + buffers[i].channelBuffers64[j] = 0; + } + else + { + if (bufferSamples > 0) + buffers[i].channelBuffers32[j] = new Sample32[bufferSamples]; + else + buffers[i].channelBuffers32[j] = 0; + } + } + } + } + } + } + return busCount; +} + +//----------------------------------------------------------------------------- +void HostProcessData::destroyBuffers (AudioBusBuffers*& buffers, int32& busCount) +{ + if (buffers) + { + for (int32 i = 0; i < busCount; i++) + { + if (channelBufferOwner) + { + for (int32 j = 0; j < buffers[i].numChannels; j++) + { + if (symbolicSampleSize == kSample64) + { + if (buffers[i].channelBuffers64[j]) + delete[] buffers[i].channelBuffers64[j]; + } + else + { + if (buffers[i].channelBuffers32[j]) + delete[] buffers[i].channelBuffers32[j]; + } + } + } + + if (symbolicSampleSize == kSample64) + { + if (buffers[i].channelBuffers64) + delete[] buffers[i].channelBuffers64; + } + else + { + if (buffers[i].channelBuffers32) + delete[] buffers[i].channelBuffers32; + } + } + + delete[] buffers; + buffers = 0; + } + busCount = 0; +} + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/hosting/processdata.h b/source/includes/vst3sdk/public.sdk/source/vst/hosting/processdata.h new file mode 100644 index 000000000..36c0ca837 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/hosting/processdata.h @@ -0,0 +1,199 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/hosting/processdata.h +// Created by : Steinberg, 10/2005 +// Description : VST Hosting Utilities +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "pluginterfaces/vst/ivstcomponent.h" +#include "pluginterfaces/vst/ivstaudioprocessor.h" + +//------------------------------------------------------------------------ +namespace Steinberg { +namespace Vst { + +//------------------------------------------------------------------------ +/** Implementation's example of ProcessData. +\ingroup hostingBase +*/ +class HostProcessData : public ProcessData +{ +public: +//------------------------------------------------------------------------ + HostProcessData () : channelBufferOwner (false) {} + virtual ~HostProcessData (); + + /** Prepare buffer containers for all buses. If bufferSamples is not null buffers will be + * created. */ + bool prepare (IComponent& component, int32 bufferSamples = 0, + int32 _symbolicSampleSize = kSample32); + + /** Remove bus buffers. */ + void unprepare (); + + /** Sets one sample buffer for all channels inside a bus. */ + void setChannelBuffers (BusDirection dir, int32 busIndex, Sample32* sampleBuffer); + void setChannelBuffers64 (BusDirection dir, int32 busIndex, Sample64* sampleBuffer); + + /** Sets individual sample buffers per channel inside a bus. */ + void setChannelBuffers (BusDirection dir, int32 busIndex, Sample32* sampleBuffers[], + int32 bufferCount); + void setChannelBuffers64 (BusDirection dir, int32 busIndex, Sample64* sampleBuffers[], + int32 bufferCount); + + /** Sets one sample buffer for a given channel inside a bus. */ + void setChannelBuffer (BusDirection dir, int32 busIndex, int32 channelIndex, + Sample32* sampleBuffer); + void setChannelBuffer64 (BusDirection dir, int32 busIndex, int32 channelIndex, + Sample64* sampleBuffer); + + static const uint64 kAllChannelsSilent = +#if MAC + 0xffffffffffffffffULL; +#else + 0xffffffffffffffffUL; +#endif + +//------------------------------------------------------------------------ +protected: + int32 createBuffers (IComponent& component, AudioBusBuffers*& buffers, BusDirection dir, + int32 bufferSamples); + void destroyBuffers (AudioBusBuffers*& buffers, int32& busCount); + bool checkIfReallocationNeeded (IComponent& component, int32 bufferSamples, + int32 _symbolicSampleSize); + + bool channelBufferOwner; +}; + +//------------------------------------------------------------------------ +// inline +//------------------------------------------------------------------------ +inline void HostProcessData::setChannelBuffers (BusDirection dir, int32 busIndex, + Sample32* sampleBuffer) +{ + if (channelBufferOwner) + return; + if (dir == kInput && (!inputs || busIndex >= numInputs)) + return; + if (dir == kOutput && (!outputs || busIndex >= numOutputs)) + return; + + AudioBusBuffers& busBuffers = dir == kInput ? inputs[busIndex] : outputs[busIndex]; + for (int32 i = 0; i < busBuffers.numChannels; i++) + busBuffers.channelBuffers32[i] = sampleBuffer; +} + +//------------------------------------------------------------------------ +inline void HostProcessData::setChannelBuffers64 (BusDirection dir, int32 busIndex, + Sample64* sampleBuffer) +{ + if (channelBufferOwner) + return; + if (dir == kInput && (!inputs || busIndex >= numInputs)) + return; + if (dir == kOutput && (!outputs || busIndex >= numOutputs)) + return; + + AudioBusBuffers& busBuffers = dir == kInput ? inputs[busIndex] : outputs[busIndex]; + for (int32 i = 0; i < busBuffers.numChannels; i++) + busBuffers.channelBuffers64[i] = sampleBuffer; +} + +//------------------------------------------------------------------------ +inline void HostProcessData::setChannelBuffers (BusDirection dir, int32 busIndex, + Sample32* sampleBuffers[], int32 bufferCount) +{ + if (channelBufferOwner) + return; + if (dir == kInput && (!inputs || busIndex >= numInputs)) + return; + if (dir == kOutput && (!outputs || busIndex >= numOutputs)) + return; + + AudioBusBuffers& busBuffers = dir == kInput ? inputs[busIndex] : outputs[busIndex]; + int32 count = bufferCount < busBuffers.numChannels ? bufferCount : busBuffers.numChannels; + for (int32 i = 0; i < count; i++) + busBuffers.channelBuffers32[i] = sampleBuffers ? sampleBuffers[i] : 0; +} + +//------------------------------------------------------------------------ +inline void HostProcessData::setChannelBuffers64 (BusDirection dir, int32 busIndex, + Sample64* sampleBuffers[], int32 bufferCount) +{ + if (channelBufferOwner) + return; + if (dir == kInput && (!inputs || busIndex >= numInputs)) + return; + if (dir == kOutput && (!outputs || busIndex >= numOutputs)) + return; + + AudioBusBuffers& busBuffers = dir == kInput ? inputs[busIndex] : outputs[busIndex]; + int32 count = bufferCount < busBuffers.numChannels ? bufferCount : busBuffers.numChannels; + for (int32 i = 0; i < count; i++) + busBuffers.channelBuffers64[i] = sampleBuffers ? sampleBuffers[i] : 0; +} + +//------------------------------------------------------------------------ +inline void HostProcessData::setChannelBuffer (BusDirection dir, int32 busIndex, int32 channelIndex, + Sample32* sampleBuffer) +{ + if (dir == kInput && (!inputs || busIndex >= numInputs)) + return; + if (dir == kOutput && (!outputs || busIndex >= numOutputs)) + return; + + AudioBusBuffers& busBuffers = dir == kInput ? inputs[busIndex] : outputs[busIndex]; + if (channelIndex >= busBuffers.numChannels) + return; + busBuffers.channelBuffers32[channelIndex] = sampleBuffer; +} + +//------------------------------------------------------------------------ +inline void HostProcessData::setChannelBuffer64 (BusDirection dir, int32 busIndex, + int32 channelIndex, Sample64* sampleBuffer) +{ + if (dir == kInput && (!inputs || busIndex >= numInputs)) + return; + if (dir == kOutput && (!outputs || busIndex >= numOutputs)) + return; + + AudioBusBuffers& busBuffers = dir == kInput ? inputs[busIndex] : outputs[busIndex]; + if (channelIndex >= busBuffers.numChannels) + return; + busBuffers.channelBuffers64[channelIndex] = sampleBuffer; +} + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/hosting/stringconvert.cpp b/source/includes/vst3sdk/public.sdk/source/vst/hosting/stringconvert.cpp new file mode 100644 index 000000000..af37f25c9 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/hosting/stringconvert.cpp @@ -0,0 +1,148 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/hosting/stringconvert.cpp +// Created by : Steinberg, 11/2014 +// Description : c++11 unicode string convert functions +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "stringconvert.h" +#include +#include +#include + +//------------------------------------------------------------------------ +namespace VST3 { +namespace StringConvert { + +//------------------------------------------------------------------------ +namespace { + +#if defined(_MSC_VER) && _MSC_VER >= 1900 +#define USE_WCHAR_AS_UTF16TYPE +using UTF16Type = wchar_t; +#else +using UTF16Type = char16_t; +#endif + +using Coverter = std::wstring_convert, UTF16Type>; + +//------------------------------------------------------------------------ +Coverter& converter () +{ + static Coverter conv; + return conv; +} + +//------------------------------------------------------------------------ +} // anonymous + +//------------------------------------------------------------------------ +std::u16string convert (const std::string& utf8Str) +{ +#if defined(USE_WCHAR_AS_UTF16TYPE) + auto wstr = converter ().from_bytes (utf8Str); + return {wstr.data (), wstr.data () + wstr.size ()}; +#else + return converter ().from_bytes (utf8Str); +#endif +} + +//------------------------------------------------------------------------ +bool convert (const std::string& utf8Str, Steinberg::Vst::String128 str) +{ + return convert (utf8Str, str, 128); +} + +//------------------------------------------------------------------------ +bool convert (const std::string& utf8Str, Steinberg::Vst::TChar* str, uint32_t maxCharacters) +{ + auto ucs2 = convert (utf8Str); + if (ucs2.length () < maxCharacters) + { + ucs2.copy (reinterpret_cast (str), ucs2.length ()); + str[ucs2.length ()] = 0; + return true; + } + return false; +} + +//------------------------------------------------------------------------ +std::string convert (const Steinberg::Vst::TChar* str) +{ + return converter ().to_bytes (reinterpret_cast (str)); +} + +//------------------------------------------------------------------------ +std::string convert (const Steinberg::Vst::TChar* str, uint32_t max) +{ + std::string result; + if (str) + { + Steinberg::Vst::TChar tmp[2] {}; + for (uint32_t i = 0; i < max; ++i, ++str) + { + tmp[0] = *str; + if (tmp[0] == 0) + break; + result += convert (tmp); + } + } + return result; +} + +//------------------------------------------------------------------------ +std::string convert (const std::u16string& str) +{ + return converter ().to_bytes (reinterpret_cast (str.data ()), + reinterpret_cast (str.data () + str.size ())); +} + +//------------------------------------------------------------------------ +std::string convert (const char* str, uint32_t max) +{ + std::string result; + if (str) + { + result.reserve (max); + for (uint32_t i = 0; i < max; ++i, ++str) + { + if (*str == 0) + break; + result += *str; + } + } + return result; +} + +//------------------------------------------------------------------------ +} // String +} // VST3 diff --git a/source/includes/vst3sdk/public.sdk/source/vst/hosting/stringconvert.h b/source/includes/vst3sdk/public.sdk/source/vst/hosting/stringconvert.h new file mode 100644 index 000000000..6acb50e6c --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/hosting/stringconvert.h @@ -0,0 +1,132 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : +// Filename : public.sdk/source/vst/hosting/stringconvert.h +// Created by : Steinberg, 11/2014 +// Description : c++11 unicode string convert functions +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "pluginterfaces/vst/vsttypes.h" +#include + +//------------------------------------------------------------------------ +namespace VST3 { +namespace StringConvert { + +//------------------------------------------------------------------------ +/** + * convert an UTF-8 string to an UTF-16 string + * + * @param utf8Str UTF-8 string + * + * @return UTF-16 string + */ +extern std::u16string convert (const std::string& utf8Str); + +//------------------------------------------------------------------------ +/** + * convert an UTF-8 string to an UTF-16 string buffer with max 127 characters + * + * @param utf8Str UTF-8 string + * @param str UTF-16 string buffer + * + * @return true on success + */ +extern bool convert (const std::string& utf8Str, Steinberg::Vst::String128 str); + +//------------------------------------------------------------------------ +/** + * convert an UTF-8 string to an UTF-16 string buffer + * + * @param utf8Str UTF-8 string + * @param str UTF-16 string buffer + * @param maxCharacters max characters that fit into str + * + * @return true on success + */ +extern bool convert (const std::string& utf8Str, Steinberg::Vst::TChar* str, + uint32_t maxCharacters); + +//------------------------------------------------------------------------ +/** + * convert an UTF-16 string buffer to an UTF-8 string + * + * @param str UTF-16 string buffer + * + * @return UTF-8 string + */ +extern std::string convert (const Steinberg::Vst::TChar* str); + +//------------------------------------------------------------------------ +/** + * convert an UTF-16 string buffer to an UTF-8 string + * + * @param str UTF-16 string buffer + * @param max maximum characters in str + * + * @return UTF-8 string + */ +extern std::string convert (const Steinberg::Vst::TChar* str, uint32_t max); + +//------------------------------------------------------------------------ +/** + * convert an UTF-16 string to an UTF-8 string + * + * @param str UTF-16 string + * + * @return UTF-8 string + */ +extern std::string convert (const std::u16string& str); + +//------------------------------------------------------------------------ +/** + * convert a ASCII string buffer to an UTF-8 string + * + * @param str ASCII string buffer + * @param max maximum characters in str + * + * @return UTF-8 string + */ +extern std::string convert (const char* str, uint32_t max); + +//------------------------------------------------------------------------ +} // String + +//------------------------------------------------------------------------ +inline const Steinberg::Vst::TChar* toTChar (const std::u16string& str) +{ + return reinterpret_cast (str.data ()); +} + +//------------------------------------------------------------------------ +} // VST3 diff --git a/source/includes/vst3sdk/public.sdk/source/vst/hosting/uid.h b/source/includes/vst3sdk/public.sdk/source/vst/hosting/uid.h new file mode 100644 index 000000000..44ed2c047 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/hosting/uid.h @@ -0,0 +1,292 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : +// Filename : public.sdk/source/vst/hosting/uid.h +// Created by : Steinberg, 08/2016 +// Description : UID +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "optional.h" +#include "pluginterfaces/base/funknown.h" +#include + +//------------------------------------------------------------------------ +namespace VST3 { + +//------------------------------------------------------------------------ +struct UID +{ +#if defined(WINDOWS) && WINDOWS == 1 + static constexpr bool defaultComFormat = true; +#else + static constexpr bool defaultComFormat = false; +#endif + + using TUID = Steinberg::TUID; + + constexpr UID () noexcept = default; + UID (uint32_t l1, uint32_t l2, uint32_t l3, uint32_t l4, bool comFormat = defaultComFormat) + noexcept; + UID (const TUID& uid) noexcept; + UID (const UID& uid) noexcept; + UID& operator= (const UID& uid) noexcept; + UID& operator= (const TUID& uid) noexcept; + + constexpr const TUID& data () const noexcept; + constexpr size_t size () const noexcept; + + std::string toString (bool comFormat = defaultComFormat) const noexcept; + static Optional fromString (const std::string& str, + bool comFormat = defaultComFormat) noexcept; + + static UID fromTUID (const TUID _uid) noexcept; +//------------------------------------------------------------------------ +private: + Steinberg::TUID _data {}; + + struct GUID + { + uint32_t Data1; + uint16_t Data2; + uint16_t Data3; + uint8_t Data4[8]; + }; +}; + +//------------------------------------------------------------------------ +inline bool operator== (const UID& uid1, const UID& uid2) +{ + const uint64_t* p1 = reinterpret_cast (uid1.data ()); + const uint64_t* p2 = reinterpret_cast (uid2.data ()); + return p1[0] == p2[0] && p1[1] == p2[1]; +} + +//------------------------------------------------------------------------ +inline bool operator!= (const UID& uid1, const UID& uid2) +{ + return !(uid1 == uid2); +} + +//------------------------------------------------------------------------ +inline bool operator< (const UID& uid1, const UID& uid2) +{ + const uint64_t* p1 = reinterpret_cast (uid1.data ()); + const uint64_t* p2 = reinterpret_cast (uid2.data ()); + return (p1[0] < p2[0]) && (p1[1] < p2[1]); +} + +//------------------------------------------------------------------------ +inline UID::UID (uint32_t l1, uint32_t l2, uint32_t l3, uint32_t l4, bool comFormat) noexcept +{ + if (comFormat) + { + _data[0] = static_cast ((l1 & 0x000000FF)); + _data[1] = static_cast ((l1 & 0x0000FF00) >> 8); + _data[2] = static_cast ((l1 & 0x00FF0000) >> 16); + _data[3] = static_cast ((l1 & 0xFF000000) >> 24); + _data[4] = static_cast ((l2 & 0x00FF0000) >> 16); + _data[5] = static_cast ((l2 & 0xFF000000) >> 24); + _data[6] = static_cast ((l2 & 0x000000FF)); + _data[7] = static_cast ((l2 & 0x0000FF00) >> 8); + _data[8] = static_cast ((l3 & 0xFF000000) >> 24); + _data[9] = static_cast ((l3 & 0x00FF0000) >> 16); + _data[10] = static_cast ((l3 & 0x0000FF00) >> 8); + _data[11] = static_cast ((l3 & 0x000000FF)); + _data[12] = static_cast ((l4 & 0xFF000000) >> 24); + _data[13] = static_cast ((l4 & 0x00FF0000) >> 16); + _data[14] = static_cast ((l4 & 0x0000FF00) >> 8); + _data[15] = static_cast ((l4 & 0x000000FF)); + } + else + { + _data[0] = static_cast ((l1 & 0xFF000000) >> 24); + _data[1] = static_cast ((l1 & 0x00FF0000) >> 16); + _data[2] = static_cast ((l1 & 0x0000FF00) >> 8); + _data[3] = static_cast ((l1 & 0x000000FF)); + _data[4] = static_cast ((l2 & 0xFF000000) >> 24); + _data[5] = static_cast ((l2 & 0x00FF0000) >> 16); + _data[6] = static_cast ((l2 & 0x0000FF00) >> 8); + _data[7] = static_cast ((l2 & 0x000000FF)); + _data[8] = static_cast ((l3 & 0xFF000000) >> 24); + _data[9] = static_cast ((l3 & 0x00FF0000) >> 16); + _data[10] = static_cast ((l3 & 0x0000FF00) >> 8); + _data[11] = static_cast ((l3 & 0x000000FF)); + _data[12] = static_cast ((l4 & 0xFF000000) >> 24); + _data[13] = static_cast ((l4 & 0x00FF0000) >> 16); + _data[14] = static_cast ((l4 & 0x0000FF00) >> 8); + _data[15] = static_cast ((l4 & 0x000000FF)); + } +} + +//------------------------------------------------------------------------ +inline UID::UID (const TUID& uid) noexcept +{ + *this = uid; +} + +//------------------------------------------------------------------------ +inline UID::UID (const UID& uid) noexcept +{ + *this = uid; +} + +//------------------------------------------------------------------------ +inline UID& UID::operator= (const UID& uid) noexcept +{ + *this = uid.data (); + return *this; +} + +//------------------------------------------------------------------------ +inline UID& UID::operator= (const TUID& uid) noexcept +{ + uint64_t* p1 = reinterpret_cast (_data); + const uint64_t* p2 = reinterpret_cast (uid); + p1[0] = p2[0]; + p1[1] = p2[1]; + return *this; +} + +//------------------------------------------------------------------------ +inline constexpr auto UID::data () const noexcept -> const TUID& +{ + return _data; +} + +//------------------------------------------------------------------------ +inline constexpr size_t UID::size () const noexcept +{ + return sizeof (TUID); +} + +//------------------------------------------------------------------------ +inline std::string UID::toString (bool comFormat) const noexcept +{ + std::string result; + result.reserve (32); + if (comFormat) + { + const auto& g = reinterpret_cast (_data); + + char tmp[21] {}; + sprintf (tmp, "%08X%04X%04X", g->Data1, g->Data2, g->Data3); + result = tmp; + + for (uint32_t i = 0; i < 8; ++i) + { + char s[3] {}; + sprintf (s, "%02X", g->Data4[i]); + result += s; + } + } + else + { + for (uint32_t i = 0; i < 16; ++i) + { + char s[3] {}; + sprintf (s, "%02X", static_cast (_data[i])); + result += s; + } + } + return result; +} + +//------------------------------------------------------------------------ +inline Optional UID::fromString (const std::string& str, bool comFormat) noexcept +{ + if (str.length () != 32) + return {}; + // TODO: this is a copy from FUID. there are no input validation checks !!! + if (comFormat) + { + TUID uid {}; + GUID g; + char s[33]; + + strcpy (s, str.data ()); + s[8] = 0; + sscanf (s, "%x", &g.Data1); + strcpy (s, str.data () + 8); + s[4] = 0; + sscanf (s, "%hx", &g.Data2); + strcpy (s, str.data () + 12); + s[4] = 0; + sscanf (s, "%hx", &g.Data3); + + memcpy (uid, &g, 8); + + for (uint32_t i = 8; i < 16; ++i) + { + char s2[3] {}; + s2[0] = str[i * 2]; + s2[1] = str[i * 2 + 1]; + + int32_t d = 0; + sscanf (s2, "%2x", &d); + uid[i] = static_cast (d); + } + return {uid}; + } + else + { + TUID uid {}; + for (uint32_t i = 0; i < 16; ++i) + { + char s[3] {}; + s[0] = str[i * 2]; + s[1] = str[i * 2 + 1]; + + int32_t d = 0; + sscanf (s, "%2x", &d); + uid[i] = static_cast (d); + } + return {uid}; + } + return {}; +} + +//------------------------------------------------------------------------ +inline UID UID::fromTUID (const TUID _uid) noexcept +{ + UID result; + + uint64_t* p1 = reinterpret_cast (result._data); + const uint64_t* p2 = reinterpret_cast (_uid); + p1[0] = p2[0]; + p1[1] = p2[1]; + + return result; +} + +//------------------------------------------------------------------------ +} // VST3 diff --git a/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/AudioIO.h b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/AudioIO.h new file mode 100644 index 000000000..0d7924c2e --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/AudioIO.h @@ -0,0 +1,151 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/interappaudio/AudioIO.h +// Created by : Steinberg, 08/2013. +// Description : VST 3 InterAppAudio +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +/// \cond ignore + +#include +#include +#include "pluginterfaces/base/funknown.h" +#include "pluginterfaces/vst/vsttypes.h" +#include + +#ifndef __OBJC__ +struct UIImage; +struct NSString; +#endif + +namespace Steinberg { +namespace Vst { +namespace InterAppAudio { + +class AudioIO; + +//------------------------------------------------------------------------ +class IMidiProcessor +{ +public: + virtual void onMIDIEvent (UInt32 status, UInt32 data1, UInt32 data2, UInt32 sampleOffset, bool withinRealtimeThread) = 0; +}; + +//------------------------------------------------------------------------ +class IAudioIOProcessor : public IMidiProcessor +{ +public: + virtual void willStartAudio (AudioIO* audioIO) = 0; + virtual void didStopAudio (AudioIO* audioIO) = 0; + + virtual void process (const AudioTimeStamp* timeStamp, UInt32 busNumber, UInt32 numFrames, AudioBufferList* ioData, bool& outputIsSilence, AudioIO* audioIO) = 0; +}; + +//------------------------------------------------------------------------ +class AudioIO +{ +public: + static AudioIO* instance (); + + tresult init (OSType type, OSType subType, OSType manufacturer, CFStringRef name); + + bool switchToHost (); + bool sendRemoteControlEvent (AudioUnitRemoteControlEvent event); + UIImage* getHostIcon (); + + tresult start (); + tresult stop (); + + tresult addProcessor (IAudioIOProcessor* processor); + tresult removeProcessor (IAudioIOProcessor* processor); + + // accessors + AudioUnit getRemoteIO () const { return remoteIO; } + SampleRate getSampleRate () const { return sampleRate; } + bool getInterAppAudioConnected () const { return interAppAudioConnected; } + + // host context information + bool getBeatAndTempo (Float64& beat, Float64& tempo); + bool getMusicalTimeLocation (UInt32& deltaSampleOffset, Float32& timeSigNumerator, UInt32& timeSigDenominator, Float64& downBeat); + bool getTransportState (Boolean& isPlaying, Boolean& isRecording, Boolean& transportStateChanged, Float64& sampleInTimeLine, Boolean& isCycling, Float64& cycleStartBeat, Float64& cycleEndBeat); + + void setStaticFallbackTempo (Float64 tempo) {staticTempo = tempo; } + Float64 getStaticFallbackTempo () const { return staticTempo; } + + static NSString* kConnectionStateChange; +//------------------------------------------------------------------------ +protected: + AudioIO (); + ~AudioIO (); + + static OSStatus inputCallbackStatic (void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData); + static OSStatus renderCallbackStatic (void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData); + static void propertyChangeStatic (void *inRefCon, AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement); + static void midiEventCallbackStatic (void *inRefCon, UInt32 inStatus, UInt32 inData1, UInt32 inData2, UInt32 inOffsetSampleFrame); + + OSStatus inputCallback (AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData); + OSStatus renderCallback (AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData); + void midiEventCallback (UInt32 inStatus, UInt32 inData1, UInt32 inData2, UInt32 inOffsetSampleFrame); + + void remoteIOPropertyChanged (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement); + + void setAudioSessionActive (bool state); + tresult setupRemoteIO (OSType type); + tresult setupAUGraph (OSType type); + void updateInterAppAudioConnectionState (); + + AudioUnit remoteIO; + AUGraph graph; + AudioBufferList* ioBufferList; + HostCallbackInfo hostCallback; + UInt32 maxFrames; + Float64 staticTempo; + SampleRate sampleRate; + bool interAppAudioConnected; + std::vector audioProcessors; + + enum InternalState { + kUninitialized, + kInitialized, + kStarted, + }; + InternalState internalState; +}; + +//------------------------------------------------------------------------ +} // namespace InterAppAudio +} // namespace Vst +} // namespace Steinberg + +/// \endcond diff --git a/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/AudioIO.mm b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/AudioIO.mm new file mode 100644 index 000000000..9d678f2ea --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/AudioIO.mm @@ -0,0 +1,551 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/interappaudio/AudioIO.mm +// Created by : Steinberg, 08/2013. +// Description : VST 3 InterAppAudio +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#import "AudioIO.h" +#import "MidiIO.h" +#import "pluginterfaces/base/fstrdefs.h" +#import +#import +#import + +#define FORCE_INLINE __attribute__((always_inline)) + +namespace Steinberg { +namespace Vst { +namespace InterAppAudio { + +//------------------------------------------------------------------------ +static AudioBufferList* createBuffers (uint32 numChannels, uint32 maxFrames, uint32 frameSize) +{ + AudioBufferList* result = (AudioBufferList*) malloc (sizeof (AudioBufferList) + sizeof (AudioBuffer) * numChannels); + result->mNumberBuffers = numChannels; + for (int32 i = 0; i < numChannels; i++) + { + result->mBuffers[i].mDataByteSize = maxFrames * sizeof (float); + result->mBuffers[i].mData = calloc (1, result->mBuffers[i].mDataByteSize); + result->mBuffers[i].mNumberChannels = 1; + } + return result; +} + +//------------------------------------------------------------------------ +static void freeAudioBufferList (AudioBufferList* audioBufferList) +{ + for (uint32 i = 0; i < audioBufferList->mNumberBuffers; i++) + { + free (audioBufferList->mBuffers[i].mData); + } + free (audioBufferList); +} + +//------------------------------------------------------------------------ +NSString* AudioIO::kConnectionStateChange = @"AudioIO::kConnectionStateChange"; + +//------------------------------------------------------------------------ +AudioIO::AudioIO () +: remoteIO (0) +, graph (0) +, ioBufferList (0) +, maxFrames (4096) +, staticTempo (120.) +, interAppAudioConnected (false) +, internalState (kUninitialized) +{ + sampleRate = [[AVAudioSession sharedInstance] sampleRate]; + memset (&hostCallback, 0, sizeof (HostCallbackInfo)); + + MidiIO::instance (); +} + +//------------------------------------------------------------------------ +AudioIO::~AudioIO () +{ + if (ioBufferList) + freeAudioBufferList (ioBufferList); +} + +//------------------------------------------------------------------------ +AudioIO* AudioIO::instance () +{ + static AudioIO gInstance; + return &gInstance; +} + +//------------------------------------------------------------------------ +tresult AudioIO::setupRemoteIO (OSType type) +{ + if (remoteIO != 0) + { + AudioStreamBasicDescription streamFormat = {}; + streamFormat.mChannelsPerFrame = 2; + streamFormat.mSampleRate = sampleRate; + streamFormat.mFormatID = kAudioFormatLinearPCM; + streamFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved; + streamFormat.mBytesPerFrame = streamFormat.mBytesPerPacket = sizeof(Float32); + streamFormat.mBitsPerChannel = 32; + streamFormat.mFramesPerPacket = 1; + + OSStatus status = AudioUnitSetProperty (remoteIO, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, + 1, + &streamFormat, + sizeof(streamFormat)); + if (status != noErr) + return kInternalError; + + status = AudioUnitSetProperty ( remoteIO, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + 0, + &streamFormat, + sizeof(streamFormat)); + if (status != noErr) + return kInternalError; + + status = AudioUnitSetProperty ( remoteIO, + kAudioUnitProperty_MaximumFramesPerSlice, + kAudioUnitScope_Global, + 1, + &maxFrames, + sizeof(maxFrames)); + + if (status != noErr) + return kInternalError; + + bool needInput = (type == kAudioUnitType_RemoteGenerator || type == kAudioUnitType_RemoteInstrument) == false; + UInt32 flag = 1; + if (needInput) + { + // enable IO Input + status = AudioUnitSetProperty (remoteIO, + kAudioOutputUnitProperty_EnableIO, + kAudioUnitScope_Input, + 1, + &flag, + sizeof(flag)); + if (status != noErr) + return kInternalError; + } + + // enable IO Output + status = AudioUnitSetProperty (remoteIO, + kAudioOutputUnitProperty_EnableIO, + kAudioUnitScope_Output, + 0, + &flag, + sizeof(flag)); + if (status != noErr) + return kInternalError; + + AURenderCallbackStruct renderCallback = {}; + if (needInput) + { + renderCallback.inputProc = inputCallbackStatic; + renderCallback.inputProcRefCon = this; + status = AudioUnitSetProperty (remoteIO, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 1, &renderCallback, sizeof (renderCallback)); + if (status != noErr) + return kInternalError; + } + + renderCallback.inputProc = renderCallbackStatic; + renderCallback.inputProcRefCon = this; + status = AudioUnitSetProperty (remoteIO, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &renderCallback, sizeof (renderCallback)); + if (status != noErr) + return kInternalError; + + if (type == kAudioUnitType_RemoteInstrument || type == kAudioUnitType_RemoteMusicEffect) + { + AudioOutputUnitMIDICallbacks callBackStruct = {}; + callBackStruct.userData = this; + callBackStruct.MIDIEventProc = midiEventCallbackStatic; + status = AudioUnitSetProperty (remoteIO, kAudioOutputUnitProperty_MIDICallbacks, kAudioUnitScope_Global, 0, &callBackStruct, sizeof (callBackStruct)); + if (status != noErr) + { + NSLog (@"Setting MIDICallback on OutputUnit failed"); + } + } + + if (ioBufferList) + freeAudioBufferList (ioBufferList); + ioBufferList = createBuffers (streamFormat.mChannelsPerFrame, maxFrames, streamFormat.mBytesPerFrame); + if (ioBufferList == 0) + return kOutOfMemory; + + status = AudioUnitAddPropertyListener (remoteIO, kAudioUnitProperty_IsInterAppConnected, propertyChangeStatic, this); + status = AudioUnitAddPropertyListener (remoteIO, kAudioOutputUnitProperty_HostTransportState, propertyChangeStatic, this); + + return kResultTrue; + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult AudioIO::setupAUGraph (OSType type) +{ + if (graph == 0) + { + OSStatus status = NewAUGraph (&graph); + if (status != noErr) + return kInternalError; + + AudioComponentDescription iOUnitDescription; + iOUnitDescription.componentManufacturer = kAudioUnitManufacturer_Apple; + iOUnitDescription.componentFlags = 0; + iOUnitDescription.componentFlagsMask = 0; + iOUnitDescription.componentType = kAudioUnitType_Output; + iOUnitDescription.componentSubType = kAudioUnitSubType_RemoteIO; + + AUNode remoteIONode; + status = AUGraphAddNode (graph, &iOUnitDescription, &remoteIONode); + if (status != noErr) + return kInternalError; + status = AUGraphOpen(graph); + if (status != noErr) + return kInternalError; + status = AUGraphNodeInfo (graph, remoteIONode, 0, &remoteIO); + if (status != noErr) + return kInternalError; + return setupRemoteIO (type); + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +void AudioIO::updateInterAppAudioConnectionState () +{ + if (remoteIO) + { + UInt32 connected; + UInt32 dataSize = sizeof (connected); + OSStatus status = AudioUnitGetProperty(remoteIO, kAudioUnitProperty_IsInterAppConnected, kAudioUnitScope_Global, 0, &connected, &dataSize); + if (status == noErr) + { + if (interAppAudioConnected != connected) + { + if (connected) + { + UInt32 size = sizeof (HostCallbackInfo); + status = AudioUnitGetProperty (remoteIO, kAudioUnitProperty_HostCallbacks, kAudioUnitScope_Global, 0, &hostCallback, &size); + } + else + { + memset (&hostCallback, 0, sizeof (HostCallbackInfo)); + } + interAppAudioConnected = connected > 0 ? true : false; + [[NSNotificationCenter defaultCenter] postNotificationName:kConnectionStateChange object:nil]; + } + } + } +} + +//------------------------------------------------------------------------ +tresult AudioIO::init (OSType type, OSType subType, OSType manufacturer, CFStringRef name) +{ + tresult result = setupAUGraph (type); + if (result != kResultTrue) + return result; + + AudioComponentDescription desc = { type, subType, manufacturer, 0, 0 }; + OSStatus status = AudioOutputUnitPublish (&desc, name, 0, remoteIO); + if (status != noErr) + { + NSLog (@"AudioOutputUnitPublish failed with status:%d", (int)status); + } + internalState = kInitialized; + + return result; +} + +//------------------------------------------------------------------------ +void AudioIO::setAudioSessionActive (bool state) +{ + NSError* error; + + AVAudioSession *session = [AVAudioSession sharedInstance]; + [session setPreferredSampleRate: sampleRate error:&error]; + [session setCategory: AVAudioSessionCategoryPlayback withOptions: AVAudioSessionCategoryOptionMixWithOthers error:&error]; + [session setActive:(state ? YES : NO) error:&error]; +} + +//------------------------------------------------------------------------ +tresult AudioIO::start () +{ + if (internalState == kInitialized) + { + bool appIsActive = [UIApplication sharedApplication].applicationState == UIApplicationStateActive; + if (!(appIsActive || interAppAudioConnected)) + { + return kResultFalse; + } + setAudioSessionActive (true); + + Boolean graphInitialized = true; + OSStatus status = AUGraphIsInitialized (graph, &graphInitialized); + if (status != noErr) + return kInternalError; + + if (graphInitialized == false) + { + status = AUGraphInitialize (graph); + if (status != noErr) + return kInternalError; + updateInterAppAudioConnectionState (); + } + + for (auto processor : audioProcessors) + { + processor->willStartAudio (this); + } + status = AUGraphStart (graph); + if (status == noErr) + { + internalState = kStarted; + updateInterAppAudioConnectionState (); + return kResultTrue; + } + return kInternalError; + } + + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult AudioIO::stop () +{ + if (internalState == kStarted) + { + if (AUGraphStop (graph) == noErr) + { + for (auto processor : audioProcessors) + { + processor->didStopAudio (this); + } + internalState = kInitialized; + if (interAppAudioConnected == false) + setAudioSessionActive (false); + return kResultTrue; + } + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult AudioIO::addProcessor (IAudioIOProcessor* processor) +{ + if (internalState == kInitialized) + { + audioProcessors.push_back (processor); + MidiIO::instance ().addProcessor (processor); + return kResultTrue; + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult AudioIO::removeProcessor (IAudioIOProcessor* processor) +{ + if (internalState == kInitialized) + { + auto it = std::find (audioProcessors.begin (), audioProcessors.end (), processor); + if (it != audioProcessors.end ()) + { + audioProcessors.erase (it); + MidiIO::instance ().removeProcessor (processor); + return kResultTrue; + } + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +bool AudioIO::switchToHost () +{ + if (remoteIO && interAppAudioConnected) + { + CFURLRef instrumentUrl; + UInt32 dataSize = sizeof(instrumentUrl); + OSStatus result = AudioUnitGetProperty (remoteIO, kAudioUnitProperty_PeerURL, kAudioUnitScope_Global, 0, &instrumentUrl, &dataSize); + if (result == noErr) + { + [[UIApplication sharedApplication] openURL:(__bridge NSURL*)instrumentUrl]; + return true; + } + } + return false; +} + +//------------------------------------------------------------------------ +bool AudioIO::sendRemoteControlEvent (AudioUnitRemoteControlEvent event) +{ + if (remoteIO && interAppAudioConnected) + { + UInt32 controlEvent = event; + UInt32 dataSize = sizeof (controlEvent); + OSStatus status = AudioUnitSetProperty (remoteIO, kAudioOutputUnitProperty_RemoteControlToHost, kAudioUnitScope_Global, 0, &controlEvent, dataSize); + return status == noErr; + } + return false; +} + +//------------------------------------------------------------------------ +UIImage* AudioIO::getHostIcon () +{ + if (remoteIO && interAppAudioConnected) + { + return AudioOutputUnitGetHostIcon (remoteIO, 128); + } + return nil; +} + +//------------------------------------------------------------------------ +bool AudioIO::getBeatAndTempo (Float64& beat, Float64& tempo) +{ + if (hostCallback.beatAndTempoProc) + { + if (hostCallback.beatAndTempoProc (hostCallback.hostUserData, &beat, &tempo) == noErr) + return true; + } + tempo = staticTempo; + beat = 0; + return true; +} + +//------------------------------------------------------------------------ +bool AudioIO::getMusicalTimeLocation (UInt32& deltaSampleOffset, Float32& timeSigNumerator, UInt32& timeSigDenominator, Float64& downBeat) +{ + if (hostCallback.musicalTimeLocationProc) + { + if (hostCallback.musicalTimeLocationProc (hostCallback.hostUserData, &deltaSampleOffset, &timeSigNumerator, &timeSigDenominator, &downBeat) == noErr) + return true; + } + return false; +} + +//------------------------------------------------------------------------ +bool AudioIO::getTransportState (Boolean& isPlaying, Boolean& isRecording, Boolean& transportStateChanged, Float64& sampleInTimeLine, Boolean& isCycling, Float64& cycleStartBeat, Float64& cycleEndBeat) +{ + if (hostCallback.transportStateProc2) + { + if (hostCallback.transportStateProc2 (hostCallback.hostUserData, &isPlaying, &isRecording, &transportStateChanged, &sampleInTimeLine, &isCycling, &cycleStartBeat, &cycleEndBeat) == noErr) + return true; + } + return false; +} + +//------------------------------------------------------------------------ +FORCE_INLINE void AudioIO::remoteIOPropertyChanged (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement) +{ + if (inID == kAudioUnitProperty_IsInterAppConnected) + { + bool wasConnected = interAppAudioConnected; + updateInterAppAudioConnectionState (); + if (wasConnected != interAppAudioConnected) + { + stop (); + start (); + } + } +} + +//------------------------------------------------------------------------ +FORCE_INLINE OSStatus AudioIO::renderCallback (AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) +{ + if (ioData->mNumberBuffers == ioBufferList->mNumberBuffers) + { + for (uint32 i = 0; i < ioData->mNumberBuffers; i++) + { + memcpy (ioData->mBuffers[i].mData, ioBufferList->mBuffers[i].mData, ioData->mBuffers[i].mDataByteSize); + } + bool outputIsSilence = ioActionFlags ? *ioActionFlags & kAudioUnitRenderAction_OutputIsSilence : false; + for (auto processor : audioProcessors) + { + outputIsSilence = false; + processor->process (inTimeStamp, inBusNumber, inNumberFrames, ioData, outputIsSilence, this); + } + if (ioActionFlags) + { + *ioActionFlags = outputIsSilence ? kAudioUnitRenderAction_OutputIsSilence : 0; + } + } + return noErr; +} + +//------------------------------------------------------------------------ +FORCE_INLINE OSStatus AudioIO::inputCallback (AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) +{ + OSStatus status = AudioUnitRender (remoteIO, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioBufferList); + return status; +} + +//------------------------------------------------------------------------ +FORCE_INLINE void AudioIO::midiEventCallback (UInt32 inStatus, UInt32 inData1, UInt32 inData2, UInt32 inOffsetSampleFrame) +{ + for (auto processor : audioProcessors) + { + processor->onMIDIEvent (inStatus, inData1, inData2, inOffsetSampleFrame, true); + } +} + +//------------------------------------------------------------------------ +OSStatus AudioIO::inputCallbackStatic (void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) +{ + AudioIO* io = (AudioIO*)inRefCon; + return io->inputCallback (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); +} + +//------------------------------------------------------------------------ +OSStatus AudioIO::renderCallbackStatic (void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) +{ + AudioIO* io = (AudioIO*)inRefCon; + return io->renderCallback (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); +} + +//------------------------------------------------------------------------ +void AudioIO::propertyChangeStatic (void *inRefCon, AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement) +{ + AudioIO* audioIO = (AudioIO*)inRefCon; + audioIO->remoteIOPropertyChanged(inID, inScope, inElement); +} + +//------------------------------------------------------------------------ +void AudioIO::midiEventCallbackStatic (void *inRefCon, UInt32 inStatus, UInt32 inData1, UInt32 inData2, UInt32 inOffsetSampleFrame) +{ + AudioIO* audioIO = (AudioIO*)inRefCon; + audioIO->midiEventCallback(inStatus, inData1, inData2, inOffsetSampleFrame); +} + +}}} diff --git a/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/CMakeLists.txt b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/CMakeLists.txt new file mode 100644 index 000000000..2387e669b --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/CMakeLists.txt @@ -0,0 +1,29 @@ +if(MAC AND XCODE AND IOS_DEVELOPMENT_TEAM) + set(target interappaudio) + + set(${target}_sources + AudioIO.mm + AudioIO.h + HostApp.mm + HostApp.h + MidiIO.mm + MidiIO.h + PresetBrowserViewController.mm + PresetBrowserViewController.h + PresetManager.mm + PresetManager.h + PresetSaveViewController.mm + PresetSaveViewController.h + SettingsViewController.mm + SettingsViewController.h + VST3Editor.mm + VST3Editor.h + VST3Plugin.mm + VST3Plugin.h + VSTInterAppAudioAppDelegateBase.mm + VSTInterAppAudioAppDelegateBase.h + ) + add_library(${target} STATIC ${${target}_sources}) + smtg_set_platform_ios(${target}) + target_link_libraries(${target} PRIVATE sdk_ios "-framework CoreGraphics" "-framework UIKit" "-framework CoreMIDI" "-framework AudioToolbox" "-framework AVFoundation") +endif() diff --git a/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/HostApp.h b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/HostApp.h new file mode 100644 index 000000000..f5b4f4e77 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/HostApp.h @@ -0,0 +1,93 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/interappaudio/HostApp.h +// Created by : Steinberg, 08/2013. +// Description : VST 3 InterAppAudio +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +/// \cond ignore + +#import "base/source/fobject.h" +#import "public.sdk/source/vst/hosting/hostclasses.h" +#import "pluginterfaces/vst/ivstinterappaudio.h" + +namespace Steinberg { +namespace Vst { +namespace InterAppAudio { + +class VST3Plugin; + +//----------------------------------------------------------------------------- +class InterAppAudioHostApp : public FObject, public HostApplication, public IInterAppAudioHost +{ +public: +//----------------------------------------------------------------------------- + static InterAppAudioHostApp* instance (); + + void setPlugin (VST3Plugin* plugin); + VST3Plugin* getPlugin () const { return plugin; } + +//----------------------------------------------------------------------------- +// IInterAppAudioHost + tresult PLUGIN_API getScreenSize (ViewRect* size, float* scale) override; + tresult PLUGIN_API connectedToHost () override; + tresult PLUGIN_API switchToHost () override; + tresult PLUGIN_API sendRemoteControlEvent (uint32 event) override; + tresult PLUGIN_API getHostIcon (void** icon) override; + tresult PLUGIN_API scheduleEventFromUI (Event& event) override; + IInterAppAudioPresetManager* PLUGIN_API createPresetManager (const TUID& cid) override; + tresult PLUGIN_API showSettingsView () override; + +//----------------------------------------------------------------------------- +// HostApplication + tresult PLUGIN_API getName (String128 name) override; + + OBJ_METHODS(InterAppAudioHostApp, FObject) + REFCOUNT_METHODS(FObject) + DEFINE_INTERFACES + DEF_INTERFACE(IHostApplication) + DEF_INTERFACE(IInterAppAudioHost) + END_DEFINE_INTERFACES(FObject) +protected: + InterAppAudioHostApp (); + + VST3Plugin* plugin; +}; + +//------------------------------------------------------------------------ +} // namespace InterAppAudio +} // namespace Vst +} // namespace Steinberg + +/// \endcond diff --git a/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/HostApp.mm b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/HostApp.mm new file mode 100644 index 000000000..d3de84bd4 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/HostApp.mm @@ -0,0 +1,169 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/interappaudio/HostApp.mm +// Created by : Steinberg, 08/2013. +// Description : VST 3 InterAppAudio +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#import "HostApp.h" +#import "AudioIO.h" +#import "VST3Plugin.h" +#import "PresetManager.h" +#import "SettingsViewController.h" +#import "base/source/updatehandler.h" +#import "pluginterfaces/gui/iplugview.h" + +namespace Steinberg { +namespace Vst { +namespace InterAppAudio { + +//------------------------------------------------------------------------ +InterAppAudioHostApp* InterAppAudioHostApp::instance () +{ + static InterAppAudioHostApp gInstance; + return &gInstance; +} + +//----------------------------------------------------------------------------- +InterAppAudioHostApp::InterAppAudioHostApp () +: plugin (0) +{ +} + +//----------------------------------------------------------------------------- +void InterAppAudioHostApp::setPlugin (VST3Plugin* plugin) +{ + this->plugin = plugin; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API InterAppAudioHostApp::getName (String128 name) +{ + String str ("InterAppAudioHost"); + str.copyTo (name, 0, 127); + return kResultTrue; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API InterAppAudioHostApp::getScreenSize (ViewRect* size, float* scale) +{ + if (size) + { + UIScreen* screen = [UIScreen mainScreen]; + CGSize s = [screen currentMode].size; + UIWindow* window = [[[UIApplication sharedApplication] windows] objectAtIndex:0]; + if (window) + { + NSArray* subViews = [window subviews]; + if ([subViews count] == 1) + { + s = [[subViews objectAtIndex:0] bounds].size; + } + } + size->left = 0; + size->top = 0; + size->right = s.width; + size->bottom = s.height; + if (scale) + { + *scale = screen.scale; + } + return kResultTrue; + } + return kInvalidArgument; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API InterAppAudioHostApp::connectedToHost () +{ + return AudioIO::instance ()->getInterAppAudioConnected () ? kResultTrue : kResultFalse; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API InterAppAudioHostApp::switchToHost () +{ + return AudioIO::instance ()->switchToHost () ? kResultTrue : kResultFalse; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API InterAppAudioHostApp::sendRemoteControlEvent (uint32 event) +{ + return AudioIO::instance ()->sendRemoteControlEvent ( + static_cast (event)) ? + kResultTrue : + kResultFalse; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API InterAppAudioHostApp::getHostIcon (void** icon) +{ + if (icon) + { + UIImage* hostIcon = AudioIO::instance ()->getHostIcon (); + if (hostIcon) + { + CGImageRef cgImage = [hostIcon CGImage]; + if (cgImage) + { + *icon = cgImage; + return kResultTrue; + } + } + return kNotImplemented; + } + return kInvalidArgument; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API InterAppAudioHostApp::scheduleEventFromUI (Event& event) +{ + if (plugin) + { + return plugin->scheduleEventFromUI (event); + } + return kNotInitialized; +} + +//----------------------------------------------------------------------------- +IInterAppAudioPresetManager* PLUGIN_API InterAppAudioHostApp::createPresetManager (const TUID& cid) +{ + return plugin ? new PresetManager (plugin, cid) : nullptr; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API InterAppAudioHostApp::showSettingsView () +{ + showIOSettings (); + return kResultTrue; +} + +}}} diff --git a/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/LaunchScreen.storyboard b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/LaunchScreen.storyboard new file mode 100644 index 000000000..2c38241b9 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/LaunchScreen.storyboard @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/MidiIO.h b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/MidiIO.h new file mode 100644 index 000000000..65d322b5d --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/MidiIO.h @@ -0,0 +1,92 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/interappaudio/MidiIO.h +// Created by : Steinberg, 09/2013. +// Description : VST 3 InterAppAudio +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include +#include "AudioIO.h" +#include + +namespace Steinberg { +namespace Vst { +namespace InterAppAudio { + +//----------------------------------------------------------------------------- +class MidiIO +{ +public: + static MidiIO& instance (); + + bool setEnabled (bool state); + bool isEnabled () const; + + // MIDI Network is experimental, don't use yet + void setMidiNetworkEnabled (bool state); + bool isMidiNetworkEnabled () const; + void setMidiNetworkPolicy (MIDINetworkConnectionPolicy policy); + MIDINetworkConnectionPolicy getMidiNetworkPolicy () const; + + void addProcessor (IMidiProcessor* processor); + void removeProcessor (IMidiProcessor* processor); + +//----------------------------------------------------------------------------- +private: + MidiIO (); + ~MidiIO (); + + void onInput (const MIDIPacketList *pktlist); + void onSourceAdded (MIDIObjectRef source); + void onSetupChanged (); + void disconnectSources (); + + MIDIClientRef client; + MIDIPortRef inputPort; + MIDIEndpointRef destPort; + + typedef std::vector MidiProcessors; + MidiProcessors midiProcessors; + + typedef std::vector ConnectionList; + ConnectionList connectedSources; + + static void readProc (const MIDIPacketList *pktlist, void *readProcRefCon, void *srcConnRefCon); + static void notifyProc (const MIDINotification *message, void *refCon); +}; + +//------------------------------------------------------------------------ +} // namespace InterAppAudio +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/MidiIO.mm b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/MidiIO.mm new file mode 100644 index 000000000..62458d087 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/MidiIO.mm @@ -0,0 +1,223 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/interappaudio/MidiIO.mm +// Created by : Steinberg, 09/2013. +// Description : VST 3 InterAppAudio +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#import "MidiIO.h" +#import + +namespace Steinberg { +namespace Vst { +namespace InterAppAudio { + +//----------------------------------------------------------------------------- +MidiIO& MidiIO::instance () +{ + static MidiIO gInstance; + return gInstance; +} + +//----------------------------------------------------------------------------- +MidiIO::MidiIO () +: client (0) +, inputPort (0) +, destPort (0) +{ +} + +//----------------------------------------------------------------------------- +MidiIO::~MidiIO () +{ + setEnabled (false); +} + +//----------------------------------------------------------------------------- +void MidiIO::addProcessor (IMidiProcessor* processor) +{ + midiProcessors.push_back (processor); +} + +//----------------------------------------------------------------------------- +void MidiIO::removeProcessor (IMidiProcessor* processor) +{ + auto it = std::find (midiProcessors.begin (), midiProcessors.end (), processor); + if (it != midiProcessors.end ()) + { + midiProcessors.erase (it); + } +} + +//----------------------------------------------------------------------------- +bool MidiIO::isEnabled () const +{ + return client != 0; +} + +//----------------------------------------------------------------------------- +bool MidiIO::setEnabled (bool state) +{ + if (state) + { + if (client) + return true; + + OSStatus err; + NSString* name = [[NSBundle mainBundle] bundleIdentifier]; + if (err = MIDIClientCreate ((__bridge CFStringRef)name, notifyProc, this, &client) != noErr) + return false; + + if (err = MIDIInputPortCreate (client, CFSTR("Input"), readProc, this, &inputPort) != noErr) + { + MIDIClientDispose (client); + client = 0; + return false; + } + name = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"CFBundleDisplayName"]; + if (err = MIDIDestinationCreate (client, (__bridge CFStringRef)name, readProc, this, &destPort) != noErr) + { + MIDIPortDispose (inputPort); + inputPort = 0; + MIDIClientDispose (client); + client = 0; + return false; + } + } + else + { + if (client == 0) + return true; + disconnectSources (); + MIDIEndpointDispose (destPort); + destPort = 0; + MIDIPortDispose (inputPort); + inputPort = 0; + MIDIClientDispose (client); + client = 0; + } + return true; +} + +//----------------------------------------------------------------------------- +void MidiIO::setMidiNetworkEnabled (bool state) +{ + if (inputPort && isMidiNetworkEnabled() != state) + { + if (!state) + { + MIDIPortDisconnectSource (inputPort, [MIDINetworkSession defaultSession].sourceEndpoint); + } + [MIDINetworkSession defaultSession].enabled = state; + if (state) + { + MIDIPortConnectSource (inputPort, [MIDINetworkSession defaultSession].sourceEndpoint, 0); + } + } +} + +//----------------------------------------------------------------------------- +bool MidiIO::isMidiNetworkEnabled () const +{ + return [MIDINetworkSession defaultSession].isEnabled; +} + +//----------------------------------------------------------------------------- +void MidiIO::setMidiNetworkPolicy (MIDINetworkConnectionPolicy policy) +{ + [MIDINetworkSession defaultSession].connectionPolicy = policy; +} + +//----------------------------------------------------------------------------- +MIDINetworkConnectionPolicy MidiIO::getMidiNetworkPolicy () const +{ + return [MIDINetworkSession defaultSession].connectionPolicy; +} + +//----------------------------------------------------------------------------- +void MidiIO::onInput (const MIDIPacketList *pktlist) +{ + const MIDIPacket* packet = &pktlist->packet[0]; + for (UInt32 i = 0; i < pktlist->numPackets; i++) + { + for (auto processor : midiProcessors) + { + processor->onMIDIEvent (packet->data[0], packet->data[1], packet->data[2], 0, false); + } + packet = MIDIPacketNext (packet); + } +} + +//----------------------------------------------------------------------------- +void MidiIO::onSourceAdded (MIDIObjectRef source) +{ + connectedSources.push_back ((MIDIEndpointRef)source); + MIDIPortConnectSource (inputPort, (MIDIEndpointRef)source, NULL); +} + +//----------------------------------------------------------------------------- +void MidiIO::disconnectSources () +{ + for (auto source : connectedSources) + MIDIPortDisconnectSource (inputPort, source); + connectedSources.clear (); +} + +//----------------------------------------------------------------------------- +void MidiIO::onSetupChanged () +{ + disconnectSources (); + ItemCount numSources = MIDIGetNumberOfSources (); + for (ItemCount i = 0; i < numSources; i++) + { + onSourceAdded (MIDIGetSource (i)); + } +} + +//----------------------------------------------------------------------------- +void MidiIO::readProc (const MIDIPacketList *pktlist, void *readProcRefCon, void *srcConnRefCon) +{ + MidiIO* io = static_cast(readProcRefCon); + io->onInput (pktlist); +} + +//----------------------------------------------------------------------------- +void MidiIO::notifyProc (const MIDINotification *message, void *refCon) +{ + if (message->messageID == kMIDIMsgSetupChanged) + { + MidiIO* mio = (MidiIO*)refCon; + mio->onSetupChanged (); + } +} + +}}} // namespaces diff --git a/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/PresetBrowserView.xib b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/PresetBrowserView.xib new file mode 100644 index 000000000..e84441761 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/PresetBrowserView.xib @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/PresetBrowserViewController.h b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/PresetBrowserViewController.h new file mode 100644 index 000000000..e4f6ecd4b --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/PresetBrowserViewController.h @@ -0,0 +1,59 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/interappaudio/PresetBrowserViewController.h +// Created by : Steinberg, 09/2013 +// Description : VST 3 InterAppAudio +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +/// \cond ignore + +#ifdef __OBJC__ + +#import +#import + +//----------------------------------------------------------------------------- +@interface PresetBrowserViewController : UIViewController +//----------------------------------------------------------------------------- + +- (id)initWithCallback:(std::function)callback; + +- (void)setFactoryPresets:(NSArray*)factoryPresets userPresets:(NSArray*)userPresets; + +@end + +#endif // __OBJC__ + +/// \endcond + diff --git a/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/PresetBrowserViewController.mm b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/PresetBrowserViewController.mm new file mode 100644 index 000000000..0d5c600e9 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/PresetBrowserViewController.mm @@ -0,0 +1,272 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/interappaudio/PresetBrowserViewController.mm +// Created by : Steinberg, 09/2013 +// Description : VST 3 InterAppAudio +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + + +#import "PresetBrowserViewController.h" +#import "pluginterfaces/base/funknown.h" + +static NSTimeInterval kAnimationTime = 0.2; + +//------------------------------------------------------------------------ +@interface PresetBrowserViewController () +//------------------------------------------------------------------------ +{ + IBOutlet UITableView* presetTableView; + IBOutlet UIView* containerView; + + std::function callback; + Steinberg::FUID uid; +} + +@property (strong) NSArray* factoryPresets; +@property (strong) NSArray* userPresets; +@property (strong) NSArray* displayPresets; +@property (assign) BOOL editMode; + +@end + +//------------------------------------------------------------------------ +@implementation PresetBrowserViewController +//------------------------------------------------------------------------ + +//------------------------------------------------------------------------ +- (id)initWithCallback:(std::function)_callback +{ + self = [super initWithNibName:@"PresetBrowserView" bundle:nil]; + if (self) + { + callback = _callback; + + self.view.alpha = 0.; + + UIViewController* rootViewController = [[UIApplication sharedApplication].windows[0] rootViewController]; + [rootViewController addChildViewController:self]; + [rootViewController.view addSubview:self.view]; + + [UIView animateWithDuration:kAnimationTime animations:^{ + self.view.alpha = 1.; + }]; + } + return self; +} + +//------------------------------------------------------------------------ +- (void)setFactoryPresets:(NSArray*)factoryPresets userPresets:(NSArray*)userPresets +{ + self.factoryPresets = factoryPresets; + self.userPresets = userPresets; + [self updatePresetArray]; + dispatch_async (dispatch_get_main_queue (), ^{ + [presetTableView reloadData]; + }); +} + +//------------------------------------------------------------------------ +- (void)viewDidLoad +{ + [super viewDidLoad]; + + containerView.layer.shadowOpacity = 0.5; + containerView.layer.shadowOffset = CGSizeMake (5, 5); + containerView.layer.shadowRadius = 5; +} + +//------------------------------------------------------------------------ +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; +} + +//------------------------------------------------------------------------ +- (void)updatePresetArray +{ + if (self.userPresets) + { + self.displayPresets = [[self.factoryPresets arrayByAddingObjectsFromArray:self.userPresets] sortedArrayUsingComparator:^NSComparisonResult (NSURL* obj1, NSURL* obj2) { + return [[obj1 lastPathComponent] caseInsensitiveCompare:[obj2 lastPathComponent]]; + }]; + } + else + { + self.displayPresets = self.factoryPresets; + } +} + +//------------------------------------------------------------------------ +- (void)removeSelf +{ + [UIView animateWithDuration:kAnimationTime animations:^{ + self.view.alpha = 0.; + } completion:^(BOOL finished) { + [self.view removeFromSuperview]; + [self removeFromParentViewController]; + }]; +} + +//------------------------------------------------------------------------ +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + NSURL* url = [self.displayPresets objectAtIndex:indexPath.row]; + if (url) + { + callback ([[url path] UTF8String]); + } + [self removeSelf]; +} + +//------------------------------------------------------------------------ +- (IBAction)toggleEditMode:(id)sender +{ + self.editMode = !self.editMode; + if (self.editMode) + { + NSMutableArray* indexPaths = [NSMutableArray new]; + for (NSURL* url in self.factoryPresets) + { + NSUInteger index = [self.displayPresets indexOfObjectIdenticalTo:url]; + [indexPaths addObject:[NSIndexPath indexPathForRow:index inSection:0]]; + } + [presetTableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade]; + } + else + { + [self updatePresetArray]; + NSMutableArray* indexPaths = [NSMutableArray new]; + for (NSURL* url in self.factoryPresets) + { + NSUInteger index = [self.displayPresets indexOfObjectIdenticalTo:url]; + [indexPaths addObject:[NSIndexPath indexPathForRow:index inSection:0]]; + } + [presetTableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade]; + } + [presetTableView setEditing:self.editMode animated:YES]; +} + +//------------------------------------------------------------------------ +- (IBAction)cancel:(id)sender +{ + if (callback) + { + callback (0); + } + [self removeSelf]; +} + +//------------------------------------------------------------------------ +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + if (self.editMode) + { + return [self.userPresets count]; + } + return [self.displayPresets count]; +} + +//------------------------------------------------------------------------ +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"PresetBrowserCell"]; + if (cell == nil) + { + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:@"PresetBrowserCell"]; + } + + cell.backgroundColor = [UIColor clearColor]; + + NSURL* presetUrl = nil; + if (self.editMode) + { + presetUrl = [self.userPresets objectAtIndex:indexPath.row]; + cell.detailTextLabel.text = @"User"; + } + else + { + presetUrl = [self.displayPresets objectAtIndex:indexPath.row]; + if ([self.factoryPresets indexOfObject:presetUrl] == NSNotFound) + { + cell.detailTextLabel.text = @"User"; + } + else + { + cell.detailTextLabel.text = @"Factory"; + } + } + + cell.textLabel.text = [[presetUrl lastPathComponent] stringByDeletingPathExtension]; + + return cell; +} + +//------------------------------------------------------------------------ +- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath +{ + if (self.editMode) + { + return YES; + } + return NO; +} + +//------------------------------------------------------------------------ +- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath +{ + NSURL* presetUrl = [self.userPresets objectAtIndex:indexPath.row]; + if (presetUrl) + { + NSFileManager* fs = [NSFileManager defaultManager]; + NSError* error = nil; + if ([fs removeItemAtURL:presetUrl error:&error] == NO) + { + UIAlertView *alert = [[UIAlertView alloc] + initWithTitle:[error localizedDescription] + message:[error localizedRecoverySuggestion] + delegate:nil + cancelButtonTitle:NSLocalizedString(@"Dismiss", @"") + otherButtonTitles:nil]; + + [alert show]; + } + else + { + NSMutableArray* newArray = [NSMutableArray arrayWithArray:self.userPresets]; + [newArray removeObject:presetUrl]; + self.userPresets = newArray; + [presetTableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; + } + } +} + +@end diff --git a/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/PresetManager.h b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/PresetManager.h new file mode 100644 index 000000000..d36a41fcf --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/PresetManager.h @@ -0,0 +1,92 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/interappaudio/PresetManager.h +// Created by : Steinberg, 10/2013 +// Description : VST 3 InterAppAudio +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "VST3Plugin.h" +#include "pluginterfaces/vst/ivstinterappaudio.h" +#include "base/source/fstring.h" + +#if __OBJC__ +@class NSArray, PresetBrowserViewController, PresetSaveViewController; +#else +struct NSArray; +struct PresetBrowserViewController; +struct PresetSaveViewController; +#endif + +namespace Steinberg { +namespace Vst { +namespace InterAppAudio { + +class PresetManager : public FObject, public IInterAppAudioPresetManager +{ +public: + PresetManager (VST3Plugin* plugin, const TUID& cid); + + tresult PLUGIN_API runLoadPresetBrowser () override; + tresult PLUGIN_API runSavePresetBrowser () override; + tresult PLUGIN_API loadNextPreset () override; + tresult PLUGIN_API loadPreviousPreset () override; + + DEFINE_INTERFACES + DEF_INTERFACE(IInterAppAudioPresetManager) + END_DEFINE_INTERFACES(FObject) + REFCOUNT_METHODS(FObject) + +private: + enum PresetPathType { + kFactory, + kUser + }; + NSArray* getPresetPaths (PresetPathType type); + + tresult loadPreset (bool next); + tresult loadPreset (const char* path); + void savePreset (const char* path); + + VST3Plugin* plugin; + PresetBrowserViewController* visiblePresetBrowserViewController; + PresetSaveViewController* visibleSavePresetViewController; + FUID cid; + String lastPreset; +}; + +//------------------------------------------------------------------------ +} // namespace InterAppAudio +} // namespace Vst +} // namespace Steinberg + diff --git a/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/PresetManager.mm b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/PresetManager.mm new file mode 100644 index 000000000..1ea67c647 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/PresetManager.mm @@ -0,0 +1,281 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/interappaudio/PresetManager.mm +// Created by : Steinberg, 10/2013 +// Description : VST 3 InterAppAudio +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#import "PresetManager.h" +#import "public.sdk/source/vst/vstpresetfile.h" +#import "pluginterfaces/vst/ivstattributes.h" +#import "PresetBrowserViewController.h" +#import "PresetSaveViewController.h" + +namespace Steinberg { +namespace Vst { +namespace InterAppAudio { + +//----------------------------------------------------------------------------- +class PresetStream : public ReadOnlyBStream, public IStreamAttributes +{ +public: + PresetStream (IBStream* sourceStream, TSize sourceOffset, TSize sectionSize, const char* utf8Path) + : ReadOnlyBStream (sourceStream, sourceOffset, sectionSize) + , fileName (utf8Path) + { + fileName.toWideString (kCP_Utf8); + } + + virtual tresult PLUGIN_API getFileName (String128 name) + { + if (fileName.length () > 0) + { + fileName.copyTo (name, 0, 128); + return kResultTrue; + } + return kResultFalse; + } + virtual IAttributeList* PLUGIN_API getAttributes () { return 0; } + + DEF_INTERFACES_1(IStreamAttributes, ReadOnlyBStream) + REFCOUNT_METHODS(ReadOnlyBStream) +protected: + String fileName; +}; + +//----------------------------------------------------------------------------- +PresetManager::PresetManager (VST3Plugin* plugin, const TUID& cid) +: plugin (plugin) +, visiblePresetBrowserViewController (nil) +, visibleSavePresetViewController (nil) +, cid (cid) +{ + id obj = [[NSUserDefaults standardUserDefaults] objectForKey:@"PresetManager|lastPreset"]; + if (obj && [obj isKindOfClass:[NSString class]]) + { + lastPreset = [obj UTF8String]; + } +} + +//----------------------------------------------------------------------------- +NSArray* PresetManager::getPresetPaths (PresetPathType type) +{ + if (type == kFactory) + { + return [[NSBundle mainBundle] URLsForResourcesWithExtension:@"vstpreset" subdirectory:@"Presets"]; + } + NSFileManager* fs = [NSFileManager defaultManager]; + NSURL* documentsUrl = [fs URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:Nil create:YES error:NULL]; + if (documentsUrl) + { + NSMutableArray* userUrls = [NSMutableArray new]; + NSDirectoryEnumerator* enumerator = [fs enumeratorAtURL:documentsUrl includingPropertiesForKeys:nil options:NSDirectoryEnumerationSkipsSubdirectoryDescendants errorHandler:nil]; + for (NSURL* url in enumerator.allObjects) + { + if ([[url pathExtension] isEqualToString:@"vstpreset"]) + { + [userUrls addObject:url]; + } + } + return [userUrls sortedArrayUsingComparator:^NSComparisonResult (NSURL* obj1, NSURL* obj2) { + return [[obj1 lastPathComponent] caseInsensitiveCompare:[obj2 lastPathComponent]]; + }]; + } + return nil; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API PresetManager::runLoadPresetBrowser () +{ + if (visiblePresetBrowserViewController != nil) + return kResultFalse; + + addRef (); + visiblePresetBrowserViewController = [[PresetBrowserViewController alloc] initWithCallback:[this](const char* path) { + loadPreset (path); + visiblePresetBrowserViewController = nil; + release (); + }]; + addRef (); + dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + if (visiblePresetBrowserViewController) + { + [visiblePresetBrowserViewController setFactoryPresets:getPresetPaths (kFactory) userPresets:getPresetPaths (kUser)]; + } + release (); + }); + return kResultTrue; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API PresetManager::runSavePresetBrowser () +{ + if (visibleSavePresetViewController != nil) + return kResultFalse; + + addRef (); + visibleSavePresetViewController = [[PresetSaveViewController alloc] initWithCallback:[this](const char* path) { + savePreset (path); + visibleSavePresetViewController = nil; + release (); + }]; + return kResultTrue; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API PresetManager::loadNextPreset () +{ + return loadPreset (true); +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API PresetManager::loadPreviousPreset () +{ + return loadPreset (false); +} + +//----------------------------------------------------------------------------- +tresult PresetManager::loadPreset (bool next) +{ + NSArray* presets = [[getPresetPaths (kFactory) arrayByAddingObjectsFromArray:getPresetPaths (kUser)] sortedArrayUsingComparator:^NSComparisonResult (NSURL* obj1, NSURL* obj2) { + return [[obj1 lastPathComponent] caseInsensitiveCompare:[obj2 lastPathComponent]]; + }]; + + __block NSUInteger index = NSNotFound; + if (lastPreset.isEmpty() == false) + { + NSURL* lastUrl = [[NSURL fileURLWithPath:[NSString stringWithUTF8String:lastPreset]] fileReferenceURL]; + if (lastUrl) + { + [presets enumerateObjectsUsingBlock:^(NSURL* obj, NSUInteger idx, BOOL *stop) { + if ([[obj fileReferenceURL] isEqual:lastUrl]) + { + index = idx; + *stop = YES; + } + }]; + } + } + if (index == NSNotFound) + { + if (next) + index = [presets count] - 1; + else + index = 1; + } + if (index != NSNotFound) + { + if (next) + { + if (index >= [presets count] - 1) + index = 0; + else + index++; + } + else + { + if (index == 0) + index = [presets count] - 1; + else + index--; + } + return loadPreset ([[[presets objectAtIndex:index] path] UTF8String]); + } + return kResultFalse; +} + +//----------------------------------------------------------------------------- +tresult PresetManager::loadPreset (const char* path) +{ + if (path) + { + IPtr stream = owned (FileStream::open (path, "r")); + if (stream) + { + [[NSUserDefaults standardUserDefaults] setObject:[NSString stringWithUTF8String:path] forKey:@"PresetManager|lastPreset"]; + lastPreset = path; + FUnknownPtr component (plugin->getAudioProcessor ()); + IEditController* controller = plugin->getEditController (); + if (component) + { + PresetFile pf (stream); + if (!pf.readChunkList ()) + return kResultFalse; + if (pf.getClassID () != cid) + return kResultFalse; + const PresetFile::Entry* e = pf.getEntry (kComponentState); + if (e == 0) + return kResultFalse; + char* filename = strrchr (path, '/'); + if (filename) + filename++; + IPtr readOnlyBStream = owned (new PresetStream (stream, e->offset, e->size, filename)); + tresult result = component->setState (readOnlyBStream); + if ((result == kResultTrue || result == kNotImplemented) && controller) + { + readOnlyBStream->seek (0, IBStream::kIBSeekSet); + controller->setComponentState (readOnlyBStream); + if (pf.contains (kControllerState)) + { + e = pf.getEntry (kControllerState); + if (e) + { + readOnlyBStream = owned (new PresetStream (stream, e->offset, e->size, filename)); + controller->setState (readOnlyBStream); + } + } + } + return result; + } + } + } + return kResultFalse; +} + +//----------------------------------------------------------------------------- +void PresetManager::savePreset (const char* path) +{ + IBStream* stream = FileStream::open (path, "w"); + if (stream) + { + FUnknownPtr component (plugin->getAudioProcessor ()); + IEditController* controller = plugin->getEditController (); + if (component) + { + PresetFile::savePreset (stream, cid, component, controller); + } + stream->release (); + loadPreset (path); + } +} + +}}} // namespaces diff --git a/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/PresetSaveView.xib b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/PresetSaveView.xib new file mode 100644 index 000000000..d845fdfba --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/PresetSaveView.xib @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/PresetSaveViewController.h b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/PresetSaveViewController.h new file mode 100644 index 000000000..5e53b6021 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/PresetSaveViewController.h @@ -0,0 +1,54 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/interappaudio/PresetSaveViewController.h +// Created by : Steinberg, 09/2013 +// Description : VST 3 InterAppAudio +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +/// \cond ignore + +#ifdef __OBJC__ + +#import +#import + +@interface PresetSaveViewController : UIViewController + +- (id)initWithCallback:(std::function)callback; + +@end + +#endif //__OBJC__ + +/// \endcond diff --git a/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/PresetSaveViewController.mm b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/PresetSaveViewController.mm new file mode 100644 index 000000000..8d3fad8f2 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/PresetSaveViewController.mm @@ -0,0 +1,171 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/interappaudio/PresetSaveViewController.mm +// Created by : Steinberg, 09/2013 +// Description : VST 3 InterAppAudio +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#import "PresetSaveViewController.h" +#import "pluginterfaces/base/funknown.h" + +static NSTimeInterval kAnimationTime = 0.2; + +//------------------------------------------------------------------------ +@interface PresetSaveViewController () +//------------------------------------------------------------------------ +{ + IBOutlet UIView* containerView; + IBOutlet UITextField* presetName; + + std::function callback; + Steinberg::FUID uid; +} +@end + +//------------------------------------------------------------------------ +@implementation PresetSaveViewController +//------------------------------------------------------------------------ + +//------------------------------------------------------------------------ +- (id)initWithCallback:(std::function)_callback +{ + self = [super initWithNibName:@"PresetSaveView" bundle:nil]; + if (self) + { + callback = _callback; + + self.view.alpha = 0.; + + UIViewController* rootViewController = [[UIApplication sharedApplication].windows[0] rootViewController]; + [rootViewController addChildViewController:self]; + [rootViewController.view addSubview:self.view]; + + [UIView animateWithDuration:kAnimationTime animations:^{ + self.view.alpha = 1.; + } completion:^(BOOL finished) { + [self showKeyboard]; + }]; + } + return self; +} + +//------------------------------------------------------------------------ +- (void)viewDidLoad +{ + [super viewDidLoad]; + + containerView.layer.shadowOpacity = 0.5; + containerView.layer.shadowOffset = CGSizeMake (5, 5); + containerView.layer.shadowRadius = 5; +} + +//------------------------------------------------------------------------ +- (void)showKeyboard +{ + [presetName becomeFirstResponder]; +} + +//------------------------------------------------------------------------ +- (void)removeSelf +{ + [UIView animateWithDuration:kAnimationTime animations:^{ + self.view.alpha = 0.; + } completion:^(BOOL finished) { + [self.view removeFromSuperview]; + [self removeFromParentViewController]; + }]; +} + +//------------------------------------------------------------------------ +- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex +{ + if (buttonIndex != alertView.cancelButtonIndex) + { + callback ([[[self presetURL] path] UTF8String]); + [self removeSelf]; + } +} + +//------------------------------------------------------------------------ +- (NSURL*)presetURL +{ + NSFileManager* fs = [NSFileManager defaultManager]; + NSURL* documentsUrl = [fs URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:Nil create:YES error:NULL]; + if (documentsUrl) + { + NSURL* presetPath = [[documentsUrl URLByAppendingPathComponent:presetName.text] URLByAppendingPathExtension:@"vstpreset"]; + return presetPath; + } + return nil; +} + +//------------------------------------------------------------------------ +- (BOOL)textFieldShouldReturn:(UITextField *)textField +{ + if ([textField.text length] > 0) + { + [self save:textField]; + return YES; + } + return NO; +} + +//------------------------------------------------------------------------ +- (IBAction)save:(id)sender +{ + if (callback) + { + NSURL* presetPath = [self presetURL]; + NSFileManager* fs = [NSFileManager defaultManager]; + if ([fs fileExistsAtPath:[presetPath path]]) + { + // alert for overwrite + UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"A Preset with this name already exists" message:@"Save it anyway ?" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"Save", nil]; + [alert show]; + return; + } + callback ([[presetPath path] UTF8String]); + } + [self removeSelf]; +} + +//------------------------------------------------------------------------ +- (IBAction)cancel:(id)sender +{ + if (callback) + { + callback (0); + } + [self removeSelf]; +} + +@end diff --git a/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/SettingsView.xib b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/SettingsView.xib new file mode 100644 index 000000000..6dda37f94 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/SettingsView.xib @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/SettingsViewController.h b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/SettingsViewController.h new file mode 100644 index 000000000..27fd3b7da --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/SettingsViewController.h @@ -0,0 +1,49 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/interappaudio/SettingsViewController.h +// Created by : Steinberg, 09/2013 +// Description : VST 3 InterAppAudio +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#ifdef __OBJC__ + +#import + +@interface SettingsViewController : UIViewController + +@end + +#endif // __OBJC__ + +extern void showIOSettings (); diff --git a/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/SettingsViewController.mm b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/SettingsViewController.mm new file mode 100644 index 000000000..3a30ccd92 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/SettingsViewController.mm @@ -0,0 +1,144 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/interappaudio/SettingsViewController.mm +// Created by : Steinberg, 09/2013 +// Description : VST 3 InterAppAudio +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#import "SettingsViewController.h" +#import "MidiIO.h" +#import "AudioIO.h" +#import + +using namespace Steinberg::Vst::InterAppAudio; + +static const NSTimeInterval kAnimationTime = 0.2; +static const NSUInteger kMinTempo = 30; + +//------------------------------------------------------------------------ +@interface SettingsViewController () +//------------------------------------------------------------------------ +{ + IBOutlet UIView* containerView; + IBOutlet UISwitch* midiOnSwitch; + IBOutlet UIPickerView* tempoView; +} +@end + +//------------------------------------------------------------------------ +@implementation SettingsViewController +//------------------------------------------------------------------------ + +//------------------------------------------------------------------------ +- (id)init +{ + self = [super initWithNibName:@"SettingsView" bundle:nil]; + if (self) + { + // Custom initialization + } + return self; +} + +//------------------------------------------------------------------------ +- (void)viewDidLoad +{ + [super viewDidLoad]; + + containerView.layer.shadowOpacity = 0.5; + containerView.layer.shadowOffset = CGSizeMake (5, 5); + containerView.layer.shadowRadius = 5; + + midiOnSwitch.on = MidiIO::instance ().isEnabled (); + + Float64 tempo = AudioIO::instance()->getStaticFallbackTempo (); + [tempoView selectRow:tempo - kMinTempo inComponent:0 animated:YES]; +} + +//------------------------------------------------------------------------ +- (IBAction)enableMidi:(id)sender +{ + BOOL state = midiOnSwitch.on; + MidiIO::instance().setEnabled (state); +} + +//------------------------------------------------------------------------ +- (IBAction)close:(id)sender +{ + [UIView animateWithDuration:kAnimationTime animations:^{ + self.view.alpha = 0.; + } completion:^(BOOL finished) { + [self.view removeFromSuperview]; + [self removeFromParentViewController]; + }]; +} + +//------------------------------------------------------------------------ +- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component +{ + AudioIO::instance()->setStaticFallbackTempo (row + kMinTempo); +} + +//------------------------------------------------------------------------ +- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView +{ + return 1; +} + +//------------------------------------------------------------------------ +- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component +{ + return 301 - kMinTempo; +} + +//------------------------------------------------------------------------ +- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component +{ + return [@(row + kMinTempo) stringValue]; +} + +@end + +//------------------------------------------------------------------------ +void showIOSettings () +{ + SettingsViewController* controller = [[SettingsViewController alloc] init]; + controller.view.alpha = 0.; + + UIViewController* rootViewController = [[UIApplication sharedApplication].windows[0] rootViewController]; + [rootViewController addChildViewController:controller]; + [rootViewController.view addSubview:controller.view]; + + [UIView animateWithDuration:kAnimationTime animations:^{ + controller.view.alpha = 1.; + }]; +} diff --git a/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/VST3Editor.h b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/VST3Editor.h new file mode 100644 index 000000000..f107e50b1 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/VST3Editor.h @@ -0,0 +1,83 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/interappaudio/VST3Editor.h +// Created by : Steinberg, 08/2013. +// Description : VST 3 InterAppAudio +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +/// \cond ignore + +#import "base/source/fobject.h" +#import "pluginterfaces/gui/iplugview.h" +#import + +namespace Steinberg { +namespace Vst { +class IEditController; + +namespace InterAppAudio { + +//------------------------------------------------------------------------ +class VST3Editor : public FObject, public IPlugFrame +{ +public: +//------------------------------------------------------------------------ + VST3Editor (); + virtual ~VST3Editor (); + + bool init (const CGRect& frame); + bool attach (IEditController* editController); + + UIViewController* getViewController () const { return viewController; } + + OBJ_METHODS(VST3Editor, FObject) + REFCOUNT_METHODS(FObject) + DEFINE_INTERFACES + DEF_INTERFACE(IPlugFrame) + END_DEFINE_INTERFACES(FObject) +protected: + + // IPlugFrame + tresult PLUGIN_API resizeView (IPlugView* view, ViewRect* newSize) override; + + IPlugView* plugView; + UIViewController* viewController; +}; + +//------------------------------------------------------------------------ +} // namespace InterAppAudio +} // namespace Vst +} // namespace Steinberg + +/// \endcond diff --git a/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/VST3Editor.mm b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/VST3Editor.mm new file mode 100644 index 000000000..3c06924cb --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/VST3Editor.mm @@ -0,0 +1,129 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/interappaudio/VST3Editor.mm +// Created by : Steinberg, 08/2013. +// Description : VST 3 InterAppAudio +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#import "VST3Editor.h" +#import "pluginterfaces/vst/ivsteditcontroller.h" + +//------------------------------------------------------------------------ +@interface VST3EditorViewController : UIViewController +//------------------------------------------------------------------------ + +@end + +//------------------------------------------------------------------------ +@implementation VST3EditorViewController +//------------------------------------------------------------------------ + +- (BOOL)prefersStatusBarHidden { return YES; } +- (BOOL)shouldAutorotate { return YES; } +- (NSUInteger)supportedInterfaceOrientations +{ + return UIInterfaceOrientationMaskLandscapeLeft|UIInterfaceOrientationMaskLandscapeRight; +} + +@end + +namespace Steinberg { +namespace Vst { +namespace InterAppAudio { + +//------------------------------------------------------------------------ +VST3Editor::VST3Editor () +: plugView (0) +, viewController (0) +{ + +} + +//------------------------------------------------------------------------ +VST3Editor::~VST3Editor () +{ + if (plugView) + { + plugView->release (); + } +} + +//------------------------------------------------------------------------ +bool VST3Editor::init (const CGRect& frame) +{ + viewController = [VST3EditorViewController new]; + viewController.view = [[UIView alloc] initWithFrame:frame]; + return true; +} + +//------------------------------------------------------------------------ +bool VST3Editor::attach (IEditController* editController) +{ + FUnknownPtr ec2 (editController); + if (ec2) + { + ec2->setKnobMode (kLinearMode); + } + plugView = editController->createView (ViewType::kEditor); + if (plugView) + { + if (plugView->isPlatformTypeSupported (kPlatformTypeUIView) == kResultTrue) + { + plugView->setFrame (this); + if (plugView->attached ((__bridge void*)viewController.view, kPlatformTypeUIView) == kResultTrue) + { + return true; + } + } + plugView->release (); + plugView = 0; + } + + return false; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API VST3Editor::resizeView (IPlugView* view, ViewRect* newSize) +{ + if (newSize && plugView && plugView == view) + { + if (view->onSize (newSize) == kResultTrue) + return kResultTrue; + return kResultFalse; + } + return kInvalidArgument; +} + +//------------------------------------------------------------------------ +} // InterAppAudio +} // Vst +} // Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/VST3Plugin.h b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/VST3Plugin.h new file mode 100644 index 000000000..93640f505 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/VST3Plugin.h @@ -0,0 +1,144 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/interappaudio/VST3Plugin.h +// Created by : Steinberg, 08/2013. +// Description : VST 3 InterAppAudio +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +/// \cond ignore + +#import "AudioIO.h" +#import "base/source/fobject.h" +#import "base/source/timer.h" +#import "base/source/tringbuffer.h" +#import "pluginterfaces/vst/ivstaudioprocessor.h" +#import "pluginterfaces/vst/ivsteditcontroller.h" +#import "pluginterfaces/vst/ivstprocesscontext.h" +#import "public.sdk/source/vst/hosting/processdata.h" +#import "public.sdk/source/vst/hosting/parameterchanges.h" +#import "public.sdk/source/vst/hosting/eventlist.h" +#import +#import + +#ifndef __OBJC__ +struct NSData; +#endif + +namespace Steinberg { +namespace Vst { +namespace InterAppAudio { + +static const int32 kMaxUIEvents = 100; + +//------------------------------------------------------------------------ +class VST3Plugin : public FObject, public IComponentHandler, public IAudioIOProcessor, public ITimerCallback +{ +public: +//------------------------------------------------------------------------ + VST3Plugin (); + virtual ~VST3Plugin (); + + bool init (); + + IEditController* getEditController () const { return editController; } + IAudioProcessor* getAudioProcessor () const { return processor; } + + tresult scheduleEventFromUI (Event& event); + + NSData* getProcessorState (); + bool setProcessorState (NSData* data); + + NSData* getControllerState (); + bool setControllerState (NSData* data); + + OBJ_METHODS(VST3Plugin, FObject) + REFCOUNT_METHODS(FObject) + DEFINE_INTERFACES + DEF_INTERFACE(IComponentHandler) + END_DEFINE_INTERFACES(FObject) +protected: + typedef std::map NoteIDPitchMap; + typedef uint32 ChannelAndCtrlNumber; + typedef std::map MIDIControllerToParamIDMap; + + void createProcessorAndController (); + + void updateProcessContext (AudioIO* audioIO); + MIDIControllerToParamIDMap createMIDIControllerToParamIDMap (); + + // IComponentHandler + tresult PLUGIN_API beginEdit (ParamID id) override; + tresult PLUGIN_API performEdit (ParamID id, ParamValue valueNormalized) override; + tresult PLUGIN_API endEdit (ParamID id) override; + tresult PLUGIN_API restartComponent (int32 flags) override; + + // IAudioIOProcessor + void willStartAudio (AudioIO* audioIO) override; + void didStopAudio (AudioIO* audioIO) override; + void onMIDIEvent (UInt32 status, UInt32 data1, UInt32 data2, UInt32 sampleOffset, bool withinRealtimeThread) override; + void process (const AudioTimeStamp* timeStamp, UInt32 busNumber, UInt32 numFrames, AudioBufferList* ioData, bool& outputIsSilence, AudioIO* audioIO) override; + + // ITimerCallback + void onTimer (Timer* timer) override; + + IAudioProcessor* processor; + IEditController* editController; + Timer* timer; + + HostProcessData processData; + ProcessContext processContext; + ParameterChangeTransfer inputParamChangeTransfer; + ParameterChangeTransfer outputParamChangeTransfer; + ParameterChanges inputParamChanges; + ParameterChanges outputParamChanges; + EventList inputEvents; + + NoteIDPitchMap noteIDPitchMap; + std::atomic lastNodeID {0}; + + bool processing; + + MIDIControllerToParamIDMap midiControllerToParamIDMap; + + TRingBuffer uiScheduledEvents; + + static ChannelAndCtrlNumber channelAndCtrlNumber (uint16 channel, CtrlNumber ctrler) { return (channel << 16) + ctrler; } +}; + +//------------------------------------------------------------------------ +} // namespace InterAppAudio +} // namespace Vst +} // namespace Steinberg + +/// \endcond diff --git a/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/VST3Plugin.mm b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/VST3Plugin.mm new file mode 100644 index 000000000..8fc0f7b6c --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/VST3Plugin.mm @@ -0,0 +1,607 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/interappaudio/VST3Plugin.mm +// Created by : Steinberg, 08/2013. +// Description : VST 3 InterAppAudio +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#import "VST3Plugin.h" +#import "HostApp.h" +#import "pluginterfaces/base/ipluginbase.h" +#import "pluginterfaces/vst/ivstmessage.h" +#import "pluginterfaces/vst/ivstmidicontrollers.h" +#import "pluginterfaces/vst/ivstinterappaudio.h" +#import "public.sdk/source/vst/auwrapper/NSDataIBStream.h" +#import "public.sdk/source/vst/hosting/hostclasses.h" +#import "base/source/updatehandler.h" +#import + +//------------------------------------------------------------------------ +extern "C" +{ + bool bundleEntry (CFBundleRef); + bool bundleExit (void); +} + +namespace Steinberg { +namespace Vst { +namespace InterAppAudio { + +//------------------------------------------------------------------------ +__attribute__((constructor)) +static void InitUpdateHandler () +{ + UpdateHandler::instance (); +} + +//------------------------------------------------------------------------ +VST3Plugin::VST3Plugin () +: processor (0) +, editController (0) +, timer (0) +, processing (false) +{ + processData.processContext = &processContext; + processData.inputParameterChanges = &inputParamChanges; + processData.outputParameterChanges = &outputParamChanges; + processData.inputEvents = &inputEvents; + +} + +//------------------------------------------------------------------------ +VST3Plugin::~VST3Plugin () +{ +} + +//------------------------------------------------------------------------ +bool VST3Plugin::init () +{ + if (processor == 0 && editController == 0) + { + ::bundleEntry (CFBundleGetMainBundle ()); + createProcessorAndController (); + } + return processor && editController; +} + +//------------------------------------------------------------------------ +void VST3Plugin::createProcessorAndController () +{ + Steinberg::IPluginFactory* factory = GetPluginFactory (); + if (factory == 0) + return; + IComponent* component = 0; + PClassInfo classInfo; + int32 classCount = factory->countClasses (); + for (int32 i = 0; i < classCount; i++) + { + if (factory->getClassInfo (i, &classInfo) != kResultTrue) + return; + if (strcmp (classInfo.category, kVstAudioEffectClass) == 0) + { + if (factory->createInstance (classInfo.cid, IComponent::iid, (void **)&component) != kResultTrue) + { + return; + } + break; + } + } + if (component) + { + if (component->initialize (InterAppAudioHostApp::instance ()->unknownCast ()) != kResultTrue) + { + component->release (); + return; + } + if (component->queryInterface (IEditController::iid, (void**)&editController) != kResultTrue) + { + FUID controllerCID; + if (component->getControllerClassId (controllerCID) == kResultTrue && controllerCID.isValid ()) + { + if (factory->createInstance (controllerCID, IEditController::iid, (void**)&editController) != kResultTrue) + return; + editController->setComponentHandler (this); + if (editController->initialize (InterAppAudioHostApp::instance ()->unknownCast ()) != kResultTrue) + { + component->release (); + editController->release (); + editController = 0; + return; + } + FUnknownPtr compConnection (component); + FUnknownPtr ctrlerConnection (editController); + if (compConnection && ctrlerConnection) + { + compConnection->connect (ctrlerConnection); + ctrlerConnection->connect (compConnection); + } + } + else + { + component->release (); + return; + } + } + component->queryInterface (IAudioProcessor::iid, (void**)&processor); + if (processor == 0) + { + if (editController) + { + editController->release (); + editController = 0; + } + } + else + { + NSMutableData* data = [NSMutableData new]; + NSMutableDataIBStream state (data); + if (component->getState (&state) == kResultTrue) + { + state.seek (0, IBStream::kIBSeekSet); + editController->setComponentState (&state); + } + int32 paramCount = editController->getParameterCount (); + inputParamChanges.setMaxParameters (paramCount); + inputParamChangeTransfer.setMaxParameters (paramCount); + outputParamChanges.setMaxParameters (paramCount); + outputParamChangeTransfer.setMaxParameters (paramCount); + midiControllerToParamIDMap = createMIDIControllerToParamIDMap (); + uiScheduledEvents.resize (kMaxUIEvents); + } + component->release (); + } +} + +//------------------------------------------------------------------------ +VST3Plugin::MIDIControllerToParamIDMap VST3Plugin::createMIDIControllerToParamIDMap () +{ + MIDIControllerToParamIDMap newMap; + + FUnknownPtr midiMapping (editController); + if (midiMapping) + { + uint16 channelCount = 0; + FUnknownPtr component (processor); + if (component) + { + int32 busCount = component->getBusCount (kEvent, kInput); + if (busCount > 0) + { + BusInfo busInfo; + if (component->getBusInfo (kEvent, kInput, 0, busInfo) == kResultTrue) + { + channelCount = busInfo.channelCount; + } + } + } + + ParamID paramID; + for (int32 channel = 0; channel < channelCount; channel++) + { + for (CtrlNumber ctrler = 0; ctrler < kCountCtrlNumber; ctrler++) + { + if (midiMapping->getMidiControllerAssignment (0, channel, ctrler, paramID) == kResultTrue) + { + newMap.insert (std::make_pair (channelAndCtrlNumber (channel, ctrler), paramID)); + } + } + } + } + return newMap; +} + +//------------------------------------------------------------------------ +tresult VST3Plugin::scheduleEventFromUI (Event& event) +{ + if (event.type == Event::kNoteOnEvent) + event.noteOn.noteId = lastNodeID++; + return uiScheduledEvents.write (event) ? kResultTrue : kResultFalse; +} + +//------------------------------------------------------------------------ +NSData* VST3Plugin::getProcessorState () +{ + if (processor) + { + NSMutableData* data = [NSMutableData new]; + NSMutableDataIBStream state (data); + FUnknownPtr comp (processor); + if (comp->getState (&state) == kResultTrue) + { + return data; + } + } + return nil; +} + +//------------------------------------------------------------------------ +bool VST3Plugin::setProcessorState (NSData* data) +{ + if (editController && processor) + { + NSDataIBStream stream (data); + FUnknownPtr comp (processor); + if (comp->setState (&stream) == kResultTrue) + { + stream.seek (0, IBStream::kIBSeekSet); + editController->setComponentState (&stream); + return true; + } + } + + return false; +} + +//------------------------------------------------------------------------ +NSData* VST3Plugin::getControllerState () +{ + if (editController) + { + NSMutableData* data = [NSMutableData new]; + NSMutableDataIBStream state (data); + if (editController->getState (&state) == kResultTrue) + { + return data; + } + } + return nil; +} + +//------------------------------------------------------------------------ +bool VST3Plugin::setControllerState (NSData* data) +{ + if (editController) + { + NSDataIBStream stream (data); + if (editController->setState (&stream) == kResultTrue) + { + return true; + } + } + return false; +} + +//------------------------------------------------------------------------ +void VST3Plugin::willStartAudio (AudioIO* audioIO) +{ + noteIDPitchMap.clear (); + lastNodeID.store (0); + + ProcessSetup setup; + setup.processMode = kRealtime; + setup.symbolicSampleSize = kSample32; + setup.maxSamplesPerBlock = 4096;// TODO: + setup.sampleRate = audioIO->getSampleRate (); + processor->setupProcessing (setup); + SpeakerArrangement inputs[1]; + SpeakerArrangement outputs[1]; + inputs[0] = SpeakerArr::kStereo; + outputs[0] = SpeakerArr::kStereo; + processor->setBusArrangements (inputs, 1, outputs, 1); + + FUnknownPtr comp (processor); + comp->setActive (true); + + processData.prepare (*comp); + + FUnknownPtr iaaConnectionNotification (editController); + if (iaaConnectionNotification) + { + iaaConnectionNotification->onInterAppAudioConnectionStateChange (audioIO->getInterAppAudioConnected () ? true : false); + } + + timer = Timer::create (this, 16); +} + +//------------------------------------------------------------------------ +void VST3Plugin::didStopAudio (AudioIO* audioIO) +{ + processor->setProcessing (false); // TODO: currently not called in Audio Thread as it should according to the VST3 spec + processing = false; + + FUnknownPtr comp (processor); + comp->setActive (false); + timer->release (); + timer = 0; +} + +//------------------------------------------------------------------------ +void VST3Plugin::onMIDIEvent (UInt32 inStatus, UInt32 data1, UInt32 data2, UInt32 sampleOffset, bool withinRealtimeThread) +{ + Event e = {}; + e.flags = Event::kIsLive; + + uint16 status = inStatus & 0xF0; + uint16 channel = inStatus & 0x0F; + if (status == 0x90 && data2 != 0) // note on + { + auto noteID = noteIDPitchMap.find ((channel << 8) + data1); + if (noteID != noteIDPitchMap.end ()) + { + // for now, we just turn off the old note on + Event e2 = {}; + e2.type = Event::kNoteOffEvent; + e2.noteOff.noteId = noteID->second; + e2.noteOff.channel = channel; + e2.noteOff.pitch = data1; + e2.noteOff.velocity = (float)data2 / 128.f; + e2.sampleOffset = 0; + inputEvents.addEvent (e2); + noteIDPitchMap.erase (noteID); + } + e.type = Event::kNoteOnEvent; + e.noteOn.channel = channel; + e.noteOn.pitch = data1; + e.noteOn.velocity = (float)data2 / 128.f; + e.noteOn.length = -1; + e.sampleOffset = sampleOffset; + if (withinRealtimeThread) + { + e.noteOn.noteId = lastNodeID++; + inputEvents.addEvent (e); + noteIDPitchMap.insert (std::make_pair ((channel << 8) + data1, e.noteOn.noteId)); + } + else + { + scheduleEventFromUI (e); + } + } + else if (status == 0x80 || (status == 0x90 && data2 == 0)) // note off + { + auto noteID = noteIDPitchMap.find ((channel << 8) + data1); + if (noteID != noteIDPitchMap.end()) + { + e.type = Event::kNoteOffEvent; + e.noteOff.noteId = noteID->second; + e.noteOff.channel = channel; + e.noteOff.pitch = data1; + e.noteOff.velocity = (float)data2 / 128.f; + e.sampleOffset = sampleOffset; + if (withinRealtimeThread) + { + inputEvents.addEvent (e); + noteIDPitchMap.erase (noteID); + } + else + { + scheduleEventFromUI (e); + } + } + else + { + NSLog (@"NoteID not found:%d", (unsigned int)data1); + } + } + else if (status == 0xb0 && data1 < kAfterTouch) // controller + { + auto it = midiControllerToParamIDMap.find (channelAndCtrlNumber (channel, data1)); + if (it != midiControllerToParamIDMap.end ()) + { + ParamValue value = (ParamValue)data2 / 128.; + if (withinRealtimeThread) + { + int32 index; + IParamValueQueue* queue = inputParamChanges.addParameterData (it->second, index); + if (queue) + { + queue->addPoint (sampleOffset, value, index); + } + } + else + { + inputParamChangeTransfer.addChange (it->second, value, sampleOffset); + } + } + } + else if (status == 0xe0) // pitch bend + { + auto it = midiControllerToParamIDMap.find (channelAndCtrlNumber (channel, kPitchBend)); + if (it != midiControllerToParamIDMap.end ()) + { + uint16 _14bit; + _14bit = (uint16)data2; + _14bit <<= 7; + _14bit |= (uint16)data1; + ParamValue value = (double)_14bit / (double)0x3fff; + + if (withinRealtimeThread) + { + int32 index; + IParamValueQueue* queue = inputParamChanges.addParameterData (it->second, index); + if (queue) + { + queue->addPoint (sampleOffset, value, index); + } + } + else + { + inputParamChangeTransfer.addChange (it->second, value, sampleOffset); + } + } + } + else if (status == 0xd0) // aftertouch + { + auto it = midiControllerToParamIDMap.find (channelAndCtrlNumber (channel, kAfterTouch)); + if (it != midiControllerToParamIDMap.end ()) + { + ParamValue value = (ParamValue)data1 / 128.; + if (withinRealtimeThread) + { + int32 index; + IParamValueQueue* queue = inputParamChanges.addParameterData (it->second, index); + if (queue) + { + queue->addPoint (sampleOffset, value, index); + } + } + else + { + inputParamChangeTransfer.addChange (it->second, value, sampleOffset); + } + } + } +} + +//------------------------------------------------------------------------ +void VST3Plugin::process (const AudioTimeStamp* timeStamp, UInt32 busNumber, UInt32 numFrames, AudioBufferList* ioData, bool& outputIsSilence, AudioIO* audioIO) +{ + if (processing == false) + { + processor->setProcessing (true); + processing = true; + } + updateProcessContext (audioIO); + if (timeStamp) + processContext.systemTime = timeStamp->mHostTime; + + // TODO: silence state update + for (UInt32 i = 0; i < ioData->mNumberBuffers; i++) + { + processData.setChannelBuffer (kInput, 0, i, (float*)ioData->mBuffers[i].mData); + processData.setChannelBuffer (kOutput, 0, i, (float*)ioData->mBuffers[i].mData); + } + + Event e; + while (uiScheduledEvents.read (e)) + { + inputEvents.addEvent (e); + if (e.type == Event::kNoteOnEvent) + { + auto noteID = noteIDPitchMap.find ((e.noteOn.channel << 8) + e.noteOn.pitch); + if (noteID == noteIDPitchMap.end ()) + noteIDPitchMap.insert (std::make_pair ((e.noteOn.channel << 8) + e.noteOn.pitch, e.noteOn.noteId)); + } + } + + processData.numSamples = numFrames; + inputParamChangeTransfer.transferChangesTo (inputParamChanges); + if (processor->process (processData) == kResultTrue) + { + inputParamChanges.clearQueue (); + outputParamChangeTransfer.transferChangesFrom (outputParamChanges); + outputParamChanges.clearQueue (); + inputEvents.clear (); + } +} + +//------------------------------------------------------------------------ +void VST3Plugin::updateProcessContext (AudioIO* audioIO) +{ + memset (&processContext, 0, sizeof (ProcessContext)); + processContext.sampleRate = audioIO->getSampleRate (); + Float64 beat = 0., tempo = 0.; + if (audioIO->getBeatAndTempo (beat, tempo)) + { + processContext.state |= ProcessContext::kTempoValid | ProcessContext::kProjectTimeMusicValid; + processContext.tempo = tempo; + processContext.projectTimeMusic = beat; + } + else + { + processContext.state |= ProcessContext::kTempoValid; + processContext.tempo = 120.; + } + UInt32 deltaSampleOffsetToNextBeat = 0; + Float32 timeSigNumerator = 0; + UInt32 timeSigDenominator = 0; + Float64 currentMeasureDownBeat = 0; + if (audioIO->getMusicalTimeLocation (deltaSampleOffsetToNextBeat, timeSigNumerator, timeSigDenominator, currentMeasureDownBeat)) + { + processContext.state |= ProcessContext::kTimeSigValid | ProcessContext::kBarPositionValid | ProcessContext::kClockValid; + processContext.timeSigNumerator = timeSigNumerator; + processContext.timeSigDenominator = timeSigDenominator; + processContext.samplesToNextClock = deltaSampleOffsetToNextBeat; + processContext.barPositionMusic = currentMeasureDownBeat; + } + Boolean isPlaying; + Boolean isRecording; + Boolean transportStateChanged; + Float64 currentSampleInTimeLine; + Boolean isCycling; + Float64 cycleStartBeat; + Float64 cycleEndBeat; + if (audioIO->getTransportState (isPlaying, isRecording, transportStateChanged, currentSampleInTimeLine, isCycling, cycleStartBeat, cycleEndBeat)) + { + processContext.state |= ProcessContext::kCycleValid; + processContext.cycleStartMusic = cycleStartBeat; + processContext.cycleEndMusic = cycleEndBeat; + processContext.projectTimeSamples = currentSampleInTimeLine; + if (isPlaying) + processContext.state |= ProcessContext::kPlaying; + if (isCycling) + processContext.state |= ProcessContext::kCycleActive; + if (isRecording) + processContext.state |= ProcessContext::kRecording; + } +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API VST3Plugin::beginEdit (ParamID id) +{ + return kResultTrue; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API VST3Plugin::performEdit (ParamID id, ParamValue valueNormalized) +{ + inputParamChangeTransfer.addChange (id, valueNormalized, 0); + return kResultTrue; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API VST3Plugin::endEdit (ParamID id) +{ + return kResultTrue; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API VST3Plugin::restartComponent (int32 flags) +{ + tresult result = kNotImplemented; + + return result; +} + +//------------------------------------------------------------------------ +void VST3Plugin::onTimer (Timer* timer) +{ + ParamID paramID; + ParamValue paramValue; + int32 sampleOffset; + while (outputParamChangeTransfer.getNextChange (paramID, paramValue, sampleOffset)) + { + editController->setParamNormalized (paramID, paramValue); + } + UpdateHandler::instance ()->triggerDeferedUpdates (); +} + +}}} diff --git a/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/VSTInterAppAudioAppDelegateBase.h b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/VSTInterAppAudioAppDelegateBase.h new file mode 100644 index 000000000..c316110c8 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/VSTInterAppAudioAppDelegateBase.h @@ -0,0 +1,57 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/interappaudio/VSTInterAppAudioAppDelegateBase.h +// Created by : Steinberg, 08/2013. +// Description : VST 3 InterAppAudio +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#import + +//------------------------------------------------------------------------ +/** Base UIApplicationDelegate class. + * This class provides the base handling of the audio engine, plug-in and plug-in editor\n + * You should subclass it for customization\n + * Make sure to call the methods of this class if you override one in your subclass ! + */ +//------------------------------------------------------------------------ +@interface VSTInterAppAudioAppDelegateBase : UIResponder +//------------------------------------------------------------------------ +@property (strong, nonatomic) UIWindow *window; + +- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions; +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions; +- (BOOL)application:(UIApplication *)application shouldSaveApplicationState:(NSCoder *)coder; +- (BOOL)application:(UIApplication *)application shouldRestoreApplicationState:(NSCoder *)coder; +- (void)applicationDidBecomeActive:(UIApplication *)application; +- (void)applicationWillResignActive:(UIApplication *)application; + +@end diff --git a/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/VSTInterAppAudioAppDelegateBase.mm b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/VSTInterAppAudioAppDelegateBase.mm new file mode 100644 index 000000000..a0b561ee0 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/interappaudio/VSTInterAppAudioAppDelegateBase.mm @@ -0,0 +1,210 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/interappaudio/VSTInterAppAudioAppDelegateBase.mm +// Created by : Steinberg, 08/2013. +// Description : VST 3 InterAppAudio +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#import "VSTInterAppAudioAppDelegateBase.h" + +#import "public.sdk/source/vst/interappaudio/AudioIO.h" +#import "public.sdk/source/vst/interappaudio/MidiIO.h" +#import "public.sdk/source/vst/interappaudio/VST3Plugin.h" +#import "public.sdk/source/vst/interappaudio/VST3Editor.h" +#import "public.sdk/source/vst/interappaudio/HostApp.h" + +using namespace Steinberg::Vst::InterAppAudio; + +//------------------------------------------------------------------------ +static OSType fourCharCodeToOSType (NSString* inCode) +{ + OSType rval = 0; + NSData* data = [inCode dataUsingEncoding: NSMacOSRomanStringEncoding]; + [data getBytes:&rval length:sizeof(rval)]; + HTONL(rval); + return rval; +} + +//------------------------------------------------------------------------ +@interface VSTInterAppAudioAppDelegateBase () +//------------------------------------------------------------------------ +{ + VST3Plugin plugin; + VST3Editor editor; + + BOOL audioIOInitialized; +} +@end + +//------------------------------------------------------------------------ +@implementation VSTInterAppAudioAppDelegateBase +//------------------------------------------------------------------------ + +//------------------------------------------------------------------------ +- (BOOL)initAudioIO +{ + id auArray = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"AudioComponents"]; + if (auArray) + { + id desc = [auArray objectAtIndex:0]; + if (desc) + { + NSString* typeStr = [desc objectForKey:@"type"]; + NSString* subtypeStr = [desc objectForKey:@"subtype"]; + NSString* manufacturerStr = [desc objectForKey:@"manufacturer"]; + NSString* nameStr = [desc objectForKey:@"name"]; + if (typeStr && subtypeStr && manufacturerStr && nameStr) + { + OSType type = fourCharCodeToOSType (typeStr); + OSType subtype = fourCharCodeToOSType (subtypeStr); + OSType manufacturer = fourCharCodeToOSType (manufacturerStr); + AudioIO* audioIO = AudioIO::instance (); + if (audioIO->init (type, subtype, manufacturer, (__bridge CFStringRef)nameStr) == Steinberg::kResultTrue) + { + if (plugin.init ()) + { + InterAppAudioHostApp::instance ()->setPlugin (&plugin); + audioIO->addProcessor (&plugin); + audioIOInitialized = YES; + return YES; + } + } + } + } + } + return NO; +} + +//------------------------------------------------------------------------ +- (BOOL)createUI +{ + if (audioIOInitialized) + { + [UIApplication sharedApplication].statusBarHidden = YES; + self.window = [UIWindow new]; + self.window.backgroundColor = [UIColor redColor]; + CGRect screenSize = self.window.bounds; + + if (editor.init (screenSize)) + { + self.window.rootViewController = editor.getViewController (); + [self.window makeKeyAndVisible]; + if (editor.attach (plugin.getEditController ()) == false) + { + return NO; + } + } + return YES; + } + return NO; +} + +//------------------------------------------------------------------------ +- (void)savePluginState:(NSCoder*)coder +{ + NSData* processorState = plugin.getProcessorState (); + NSData* controllerState = plugin.getControllerState (); + if (processorState) + [coder encodeObject:processorState forKey:@"VST3ProcessorState"]; + if (controllerState) + [coder encodeObject:controllerState forKey:@"VST3ControllerState"]; +} + +//------------------------------------------------------------------------ +- (void)restorePluginState:(NSCoder*)coder +{ + NSData* processorState = [coder decodeObjectForKey:@"VST3ProcessorState"]; + if (processorState) + { + plugin.setProcessorState (processorState); + } + NSData* controllerState = [coder decodeObjectForKey:@"VST3ControllerState"]; + if (controllerState) + { + plugin.setControllerState (controllerState); + } +} + +//------------------------------------------------------------------------ +// UIApplicationDelegate methods +//------------------------------------------------------------------------ +- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + return [self initAudioIO]; +} + +//------------------------------------------------------------------------ +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + BOOL result = [self createUI]; + if (result) + { + AudioIO::instance ()->start (); + } + return result; +} + +//------------------------------------------------------------------------ +- (BOOL)application:(UIApplication *)application shouldSaveApplicationState:(NSCoder *)coder +{ + [self savePluginState:coder]; + [coder encodeBool:MidiIO::instance ().isEnabled () forKey:@"MIDI Enabled"]; + return YES; +} + +//------------------------------------------------------------------------ +- (BOOL)application:(UIApplication *)application shouldRestoreApplicationState:(NSCoder *)coder +{ + [self restorePluginState:coder]; + BOOL midiEnabled = [coder decodeBoolForKey:@"MIDI Enabled"]; + MidiIO::instance ().setEnabled (midiEnabled); + return YES; +} + +//------------------------------------------------------------------------ +- (void)applicationDidBecomeActive:(UIApplication *)application +{ + AudioIO* audioIO = AudioIO::instance (); + audioIO->start (); +} + +//------------------------------------------------------------------------ +- (void)applicationWillResignActive:(UIApplication *)application +{ + AudioIO* audioIO = AudioIO::instance (); + if (audioIO->getInterAppAudioConnected () == false && MidiIO::instance().isEnabled () == false) + { + audioIO->stop (); + } +} + +@end diff --git a/source/includes/vst3sdk/public.sdk/source/vst/testsuite/vsttestsuite.cpp b/source/includes/vst3sdk/public.sdk/source/vst/testsuite/vsttestsuite.cpp new file mode 100644 index 000000000..0b0bd0953 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/testsuite/vsttestsuite.cpp @@ -0,0 +1,2742 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Validator +// Filename : public.sdk/source/vst/vsttestsuite.cpp +// Created by : Steinberg, 10/2005 +// Description : VST Hosting Utilities +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "vsttestsuite.h" +#include "public.sdk/source/vst/hosting/stringconvert.h" + +#include "pluginterfaces/vst/ivstcomponent.h" +#include "pluginterfaces/vst/ivstaudioprocessor.h" +#include "pluginterfaces/vst/ivsteditcontroller.h" +#include "pluginterfaces/vst/ivstprocesscontext.h" +#include "pluginterfaces/vst/ivstmessage.h" +#include "pluginterfaces/vst/ivstunits.h" +#include "pluginterfaces/vst/ivstmidicontrollers.h" +#include "pluginterfaces/vst/ivstnoteexpression.h" +#include "pluginterfaces/vst/vstpresetkeys.h" + +#include +#include +#include +#include + +namespace Steinberg { +namespace Vst { + +#define NUM_ITERATIONS 20 +#define DEFAULT_SAMPLE_RATE 44100 +#define DEFAULT_BLOCK_SIZE 64 +#define MAX_BLOCK_SIZE 8192 +#define BUFFERS_ARE_EQUAL 0 +#define NUM_AUDIO_BLOCKS_TO_PROCESS 3 +#define CHANNEL_IS_SILENT 1 + +#define TOUGHTESTS 0 + +DEF_CLASS_IID (IPlugProvider) + +FUnknown* gStandardPluginContext = nullptr; +void setStandardPluginContext (FUnknown* context) { gStandardPluginContext = context; }; + +//------------------------------------------------------------------------ +template struct ArrayDeleter +{ + ArrayDeleter (T* array): array (array) {} + ~ArrayDeleter () { if (array) delete[] array; } + + T* array; +}; + + +//------------------------------------------------------------------------ +// VstTestBase +//------------------------------------------------------------------------ +VstTestBase::VstTestBase (IPlugProvider* plugProvider) +: plugProvider (plugProvider) +, vstPlug (nullptr) +, controller (nullptr) +{ + FUNKNOWN_CTOR + + if (plugProvider) + plugProvider->addRef (); +} + +//------------------------------------------------------------------------ +VstTestBase::VstTestBase () +: plugProvider (nullptr) +, vstPlug (nullptr) +, controller (nullptr) +{ + FUNKNOWN_CTOR +} + +//------------------------------------------------------------------------ +VstTestBase::~VstTestBase () +{ + FUNKNOWN_DTOR + + if (plugProvider) + plugProvider->release (); +} + +//------------------------------------------------------------------------ +IMPLEMENT_FUNKNOWN_METHODS (VstTestBase, ITest, ITest::iid) + +//------------------------------------------------------------------------ +bool VstTestBase::setup () +{ + if (plugProvider) + { + vstPlug = plugProvider->getComponent (); + controller = plugProvider->getController (); + + if (vstPlug) + { + vstPlug->activateBus (kAudio, kInput, 0, true); + vstPlug->activateBus (kAudio, kOutput, 0, true); + + return true; + } + } + return false; +} + +//------------------------------------------------------------------------ +bool VstTestBase::teardown () +{ + if (vstPlug) + { + if (vstPlug) + { + vstPlug->activateBus (kAudio, kInput, 0, false); + vstPlug->activateBus (kAudio, kOutput, 0, false); + } + plugProvider->releasePlugIn (vstPlug, controller); + } + return true; +} + +//------------------------------------------------------------------------ +// Component Initialize / Terminate +//------------------------------------------------------------------------ + + +//------------------------------------------------------------------------ +// VstTestEnh +//------------------------------------------------------------------------ +VstTestEnh::VstTestEnh (IPlugProvider* plugProvider, ProcessSampleSize sampl) +: VstTestBase (plugProvider) +, audioEffect (nullptr) +{ + // process setup defaults + memset (&processSetup, 0, sizeof (ProcessSetup)); + + processSetup.processMode = kRealtime; + processSetup.symbolicSampleSize = sampl; + processSetup.maxSamplesPerBlock = kMaxSamplesPerBlock; + processSetup.sampleRate = kSampleRate; +} + +//------------------------------------------------------------------------ +VstTestEnh::~VstTestEnh () +{ +} + +//------------------------------------------------------------------------ +bool VstTestEnh::setup () +{ + bool res = VstTestBase::setup (); + + if (vstPlug) + { + tresult check = vstPlug->queryInterface (IAudioProcessor::iid, (void**)&audioEffect); + if (check != kResultTrue) + return false; + } + + return (res && audioEffect); +} + +//------------------------------------------------------------------------ +bool VstTestEnh::teardown () +{ + if (audioEffect) + audioEffect->release (); + + bool res = VstTestBase::teardown (); + + return res && audioEffect; +} + +//------------------------------------------------------------------------ +static void addMessage (ITestResult* testResult, const std::u16string& str) +{ + testResult->addMessage ((const tchar*)str.data ()); +} + +//------------------------------------------------------------------------ +static void addMessage (ITestResult* testResult, const tchar* str) +{ + testResult->addMessage (str); +} + +//------------------------------------------------------------------------ +static void addErrorMessage (ITestResult* testResult, const tchar* str) +{ + testResult->addErrorMessage (str); +} + +//------------------------------------------------------------------------ +static void addErrorMessage (ITestResult* testResult, const std::u16string& str) +{ + testResult->addErrorMessage ((const tchar*)str.data ()); +} + +//------------------------------------------------------------------------ +static void printTestHeader (VstTestBase* test, ITestResult* testResult) +{ + using VST3::StringConvert::convert; + + std::string str = "==="; + str += test->getName (); + str += " ===================================="; + addMessage (testResult, convert (str)); +} + +//------------------------------------------------------------------------ +static std::u16string printf (const char8* format, ...) +{ + using VST3::StringConvert::convert; + + char8 string[1024 * 4]; + + va_list marker; + va_start (marker, format); + + vsnprintf (string, kPrintfBufferSize, format, marker); + return convert (string).data (); +} + +//------------------------------------------------------------------------ +// VstSuspendResumeTest +//------------------------------------------------------------------------ +VstSuspendResumeTest::VstSuspendResumeTest (IPlugProvider* plugProvider, ProcessSampleSize sampl) +: VstTestEnh (plugProvider, sampl) +{} + +//------------------------------------------------------------------------ +bool PLUGIN_API VstSuspendResumeTest::run (ITestResult* testResult) +{ + if (!vstPlug || !testResult) + return false; + + printTestHeader (this, testResult); + + for (int32 i = 0; i < 3; ++i) + { + if (audioEffect) + { + if (audioEffect->canProcessSampleSize (kSample32) == kResultOk) + processSetup.symbolicSampleSize = kSample32; + else if (audioEffect->canProcessSampleSize (kSample64) == kResultOk) + processSetup.symbolicSampleSize = kSample64; + else + { + addErrorMessage (testResult, STR ("No appropriate symbolic sample size supported!")); + return false; + } + + if (audioEffect->setupProcessing (processSetup) != kResultOk) + { + addErrorMessage (testResult, STR ("Process setup failed!")); + return false; + } + + } + tresult result = vstPlug->setActive (true); + if (result != kResultOk) + return false; + + result = vstPlug->setActive (false); + if (result != kResultOk) + return false; + } + return true; +} + + +//------------------------------------------------------------------------ +//------------------------------------------------------------------------ +// VstTerminateInitializeTest +//------------------------------------------------------------------------ +VstTerminateInitializeTest::VstTerminateInitializeTest (IPlugProvider* plugProvider) +: VstTestBase (plugProvider) +{} + +//------------------------------------------------------------------------ +bool VstTerminateInitializeTest::run (ITestResult* testResult) +{ + if (!vstPlug || !testResult) + return false; + + printTestHeader (this, testResult); + + bool result = true; + if (vstPlug->terminate () != kResultTrue) + { + addErrorMessage (testResult, STR ("IPluginBase::terminate () failed.")); + result = false; + } + if (vstPlug->initialize (gStandardPluginContext) != kResultTrue) + { + addErrorMessage (testResult, STR ("IPluginBase::initialize (..) failed.")); + result = false; + } + return result; +} + +//------------------------------------------------------------------------ +// VstScanBussesTest +//------------------------------------------------------------------------ +VstScanBussesTest::VstScanBussesTest (IPlugProvider* plugProvider) +: VstTestBase (plugProvider) +{} + +//------------------------------------------------------------------------ +bool PLUGIN_API VstScanBussesTest::run (ITestResult* testResult) +{ + if (!vstPlug || !testResult) + return false; + + printTestHeader (this, testResult); + + int32 numBusses = 0; + + for (MediaType mediaType = kAudio; mediaType < kNumMediaTypes; mediaType++) + { + int32 numInputs = vstPlug->getBusCount (mediaType, kInput); + int32 numOutputs = vstPlug->getBusCount (mediaType, kOutput); + + numBusses += (numInputs + numOutputs); + + if ((mediaType == (kNumMediaTypes - 1)) && (numBusses == 0)) + { + addErrorMessage (testResult, STR ("This component does not export any buses!!!")); + return false; + } + + addMessage (testResult, printf ("=> %s Buses: [%d In(s) => %d Out(s)]", mediaType == kAudio ? "Audio" : "Event", numInputs, numOutputs)); + + for (int32 i = 0; i < numInputs + numOutputs; ++i) + { + BusDirection busDirection = i < numInputs ? kInput : kOutput; + int32 busIndex = busDirection == kInput ? i : i - numInputs; + + BusInfo busInfo = {0}; + if (vstPlug->getBusInfo (mediaType, busDirection, busIndex, busInfo) == kResultTrue) + { + auto busName = VST3::StringConvert::convert (busInfo.name);; + + if (busName.empty ()) + { + addErrorMessage (testResult, printf ("Bus %d has no name!!!", busIndex)); + return false; + } + addMessage (testResult, printf (" %s[%d]: \"%s\" (%s-%s) ", + busDirection == kInput ? "In " : "Out", + busIndex, + busName.data(), + busInfo.busType == kMain ? "Main" : "Aux", + busInfo.kDefaultActive ? "Default Active" : "Default Inactive")); + } + else + return false; + } + } + return true; +} + +//------------------------------------------------------------------------ +// VstScanParametersTest +//------------------------------------------------------------------------ +VstScanParametersTest::VstScanParametersTest (IPlugProvider* plugProvider) +: VstTestBase (plugProvider) +{} + +//------------------------------------------------------------------------ +bool PLUGIN_API VstScanParametersTest::run (ITestResult* testResult) +{ + if (!testResult || !vstPlug) + return false; + + printTestHeader (this, testResult); + + if (!controller) + { + addMessage (testResult, STR ("No Edit Controller supplied!")); + return true; + } + + int32 numParameters = controller->getParameterCount (); + if (numParameters <= 0) + { + addMessage (testResult, STR ("This component does not export any parameters!")); + return true; + } + + addMessage (testResult, printf ("This component exports %d parameter(s)", numParameters)); + + FUnknownPtr iUnitInfo2 (controller); + if (!iUnitInfo2 && numParameters > 20) + { + addMessage (testResult, STR ("Note: it could be better to use UnitInfo in order to sort Parameters (>20).")); + } + + // used for ID check + int32* paramIds = new int32[numParameters]; + + bool foundBypass = false; + for (int32 i = 0; i < numParameters; ++i) + { + ParameterInfo paramInfo = {0}; + + tresult result = controller->getParameterInfo (i, paramInfo); + if (result != kResultOk) + { + addErrorMessage (testResult, printf ("Param %03d: is missing!!!", i)); + return false; + } + + int32 paramId = paramInfo.id; + paramIds[i] = paramId; + if (paramId < 0) + { + addErrorMessage (testResult, printf ("Param %03d: Invalid Id!!!", i)); + return false; + } + + // check if ID is already used by another parameter + for (int32 idIndex = 0; idIndex < i; idIndex++) + { + if (paramIds[idIndex] == paramIds[i]) + { + addErrorMessage (testResult, printf ("Param %03d: ID already used (by %03d)!!!", i, idIndex)); + return false; + } + } + + const tchar* paramType = kEmptyString; + if (paramInfo.stepCount < 0) + { + addErrorMessage (testResult, printf ("Param %03d: invalid stepcount!!!", i)); + return false; + } + if (paramInfo.stepCount == 0) + paramType = STR ("Float"); + else if (paramInfo.stepCount == 1) + paramType = STR ("Toggle"); + else + paramType = STR ("Discrete"); + + + auto paramTitle = VST3::StringConvert::convert (paramInfo.title); + auto paramUnits = VST3::StringConvert::convert (paramInfo.units); + + if (paramTitle.empty ()) + { + addErrorMessage (testResult, printf ("Param %03d: has no title!!!", i)); + return false; + } + + if (paramInfo.defaultNormalizedValue != -1.f && (paramInfo.defaultNormalizedValue < 0. || paramInfo.defaultNormalizedValue > 1.)) + { + addErrorMessage (testResult, printf ("Param %03d: defaultValue is not normalized!!!", i)); + return false; + } + + int32 unitId = paramInfo.unitId; + if (unitId < -1) + { + addErrorMessage (testResult, printf ("Param %03d: No appropriate unit ID!!!", i)); + return false; + } + if (unitId >= -1) + { + FUnknownPtr iUnitInfo (controller); + if (!iUnitInfo && unitId != 0) + { + addErrorMessage (testResult, printf ("IUnitInfo interface is missing, but ParameterInfo::unitID is not %03d (kRootUnitId).", kRootUnitId)); + return false; + } + else if (iUnitInfo) + { + bool found = false; + int32 uc = iUnitInfo->getUnitCount (); + for (int32 ui = 0; ui < uc; ++ui) + { + UnitInfo uinfo = {0}; + if (iUnitInfo->getUnitInfo (ui, uinfo) != kResultTrue) + { + addErrorMessage (testResult, STR ("IUnitInfo::getUnitInfo (..) failed.")); + return false; + } + if (uinfo.id == unitId) + found = true; + } + if (!found && unitId != kRootUnitId) + { + addErrorMessage (testResult, STR ("Parameter has a UnitID, which isn't defined in IUnitInfo.")); + return false; + } + } + } + if (((paramInfo.flags & ParameterInfo::kCanAutomate) != 0) && ((paramInfo.flags & ParameterInfo::kIsReadOnly) != 0)) + { + addErrorMessage (testResult, STR ("Parameter must not be kCanAutomate and kReadOnly at the same time.")); + return false; + } + + if ((paramInfo.flags & ParameterInfo::kIsBypass) != 0) + { + if (!foundBypass) + foundBypass = true; + else + { + addErrorMessage (testResult, STR ("There can only be one bypass (kIsBypass).")); + return false; + } + } + + addMessage (testResult, printf (" Param %03d (ID = %d): [title=\"%s\"] [unit=\"%s\"] [type = %s, default = %lf, unit = %d]", + i, paramId, paramTitle.data (), paramUnits.data (), + paramType, paramInfo.defaultNormalizedValue, unitId) + ); + } + + if (foundBypass == false) + { + addMessage (testResult, STR ("Warning: No bypass parameter found. Is this intended ?")); + } + + if (paramIds) + delete [] paramIds; + + return true; +} + +//------------------------------------------------------------------------ +// VstMidiMappingTest +//------------------------------------------------------------------------ +VstMidiMappingTest::VstMidiMappingTest (IPlugProvider* plugProvider) +: VstTestBase (plugProvider) +{} + +//------------------------------------------------------------------------ +bool PLUGIN_API VstMidiMappingTest::run (ITestResult* testResult) +{ + if (!testResult || !vstPlug) + return false; + + printTestHeader (this, testResult); + + if (!controller) + { + addMessage (testResult, STR ("No Edit Controller supplied!")); + return true; + } + + FUnknownPtr midiMapping (controller); + if (!midiMapping) + { + addMessage (testResult, STR ("No MIDI Mapping interface supplied!")); + return true; + } + + int32 numParameters = controller->getParameterCount (); + int32 eventBusCount = vstPlug->getBusCount (kEvent, kInput); + bool interruptProcess = false; + + for (int32 bus = 0; bus < eventBusCount + 1; bus++) + { + if (interruptProcess) + break; + + BusInfo info; + if (vstPlug->getBusInfo (kEvent, kInput, bus, info) == kResultTrue) + { + if (bus >= eventBusCount) + { + addMessage (testResult, STR ("getBusInfo supplied for an unknown event bus")); + break; + } + } + else + break; + + for (int16 channel = 0; channel < info.channelCount; channel++) + { + if (interruptProcess) + break; + + int32 foundCount = 0; + for (CtrlNumber cc = 0; cc < kCountCtrlNumber + 1; cc++) + { + ParamID tag; + if (midiMapping->getMidiControllerAssignment (bus, channel, cc, tag) == kResultTrue) + { + if (bus >= eventBusCount) + { + addMessage (testResult, STR ("MIDI Mapping supplied for an unknown event bus")); + interruptProcess = true; + break; + } + if (cc >= kCountCtrlNumber) + { + addMessage (testResult, STR ("MIDI Mapping supplied for an wrong ControllerNumbers value (bigger than the max)")); + break; + } + + bool foundParameter = false; + for (int32 i = 0; i < numParameters; ++i) + { + ParameterInfo info; + if (controller->getParameterInfo (i, info) == kResultTrue) + { + if (info.id == tag) + { + foundParameter = true; + break; + } + } + } + if (!foundParameter) + { + addErrorMessage (testResult, printf ("Unknown ParamID [%d] returned for MIDI Mapping", tag)); + return false; + } + foundCount++; + } + else + { + if (bus >= eventBusCount) + interruptProcess = true; + } + } + if (foundCount == 0 && (bus < eventBusCount)) + { + addMessage (testResult, printf ("MIDI Mapping getMidiControllerAssignment (%d, %d) : no assignment available!", bus, channel)); + } + } + } + + return true; +} + +//------------------------------------------------------------------------ +// VstNoteExpressionTest +//------------------------------------------------------------------------ +VstNoteExpressionTest::VstNoteExpressionTest (IPlugProvider* plugProvider) +: VstTestBase (plugProvider) +{} + +//------------------------------------------------------------------------ +bool PLUGIN_API VstNoteExpressionTest::run (ITestResult* testResult) +{ + if (!testResult || !vstPlug) + return false; + + printTestHeader (this, testResult); + + if (!controller) + { + addMessage (testResult, STR ("No Edit Controller supplied!")); + return true; + } + + FUnknownPtr noteExpression (controller); + if (!noteExpression) + { + addMessage (testResult, STR ("No Note Expression interface supplied!")); + return true; + } + + int32 eventBusCount = vstPlug->getBusCount (kEvent, kInput); + + for (int32 bus = 0; bus < eventBusCount; bus++) + { + BusInfo busInfo; + vstPlug->getBusInfo (kEvent, kInput, bus, busInfo); + + for (int16 channel = 0; channel < busInfo.channelCount; channel++) + { + int32 count = noteExpression->getNoteExpressionCount (bus, channel); + if (count > 0) + { + addMessage (testResult, printf ("Note Expression count bus[%d], channel[%d]: %d", bus, channel, count)); + } + + for (int32 i = 0; i < count; ++i) + { + NoteExpressionTypeInfo info; + if (noteExpression->getNoteExpressionInfo (bus, channel, i, info) == kResultTrue) + { + addMessage (testResult, printf ("Note Expression TypeID: %d [%s]", info.typeId, VST3::StringConvert::convert (info.title).data ())); + NoteExpressionTypeID id = info.typeId; + NoteExpressionValue valueNormalized = info.valueDesc.defaultValue; + String128 string; + if (noteExpression->getNoteExpressionStringByValue (bus, channel, id, valueNormalized, string) != kResultTrue) + { + addMessage (testResult, printf ("Note Expression getNoteExpressionStringByValue (%d, %d, %d) return kResultFalse!", bus, channel, id)); + } + + if (noteExpression->getNoteExpressionValueByString (bus, channel, id, string, valueNormalized) != kResultTrue) + { + addMessage (testResult, printf ("Note Expression getNoteExpressionValueByString (%d, %d, %d) return kResultFalse!", bus, channel, id)); + } + } + else + { + addErrorMessage (testResult, printf ("Note Expression getNoteExpressionInfo (%d, %d, %d) return kResultFalse!", bus, channel, i)); + return false; + } + } + } + } + + return true; +} + + + +//------------------------------------------------------------------------ +// VstKeyswitchTest +//------------------------------------------------------------------------ +VstKeyswitchTest::VstKeyswitchTest (IPlugProvider* plugProvider) +: VstTestBase (plugProvider) +{} + +//------------------------------------------------------------------------ +bool PLUGIN_API VstKeyswitchTest::run (ITestResult* testResult) +{ + if (!testResult || !vstPlug) + return false; + + printTestHeader (this, testResult); + + if (!controller) + { + addMessage (testResult, STR ("No Edit Controller supplied!")); + return true; + } + + FUnknownPtr keyswitch (controller); + if (!keyswitch) + { + addMessage (testResult, STR ("No Keyswitch interface supplied!")); + return true; + } + + int32 eventBusCount = vstPlug->getBusCount (kEvent, kInput); + + for (int32 bus = 0; bus < eventBusCount; bus++) + { + BusInfo busInfo; + vstPlug->getBusInfo (kEvent, kInput, bus, busInfo); + + for (int16 channel = 0; channel < busInfo.channelCount; channel++) + { + int32 count = keyswitch->getKeyswitchCount (bus, channel); + + if (count > 0) + { + addMessage (testResult, printf ("Keyswitch support bus[%d], channel[%d]: %d", bus, channel, count)); + } + + for (int32 i = 0; i < count; ++i) + { + KeyswitchInfo info; + if (keyswitch->getKeyswitchInfo (bus, channel, i, info) == kResultTrue) + { + } + else + { + addErrorMessage (testResult, printf ("Keyswitch getKeyswitchInfo (%d, %d, %d) return kResultFalse!", bus, channel, i)); + return false; + } + } + } + } + + return true; +} + +//------------------------------------------------------------------------ +// VstEditorClassesTest +//------------------------------------------------------------------------ +VstEditorClassesTest::VstEditorClassesTest (IPlugProvider* plugProvider) +: VstTestBase (plugProvider) +{} + +//------------------------------------------------------------------------ +bool PLUGIN_API VstEditorClassesTest::run (ITestResult* testResult) +{ + if (!vstPlug || !testResult) + return false; + + printTestHeader (this, testResult); + + // no controller is allowed... + if (FUnknownPtr (vstPlug).getInterface ()) + { + addMessage (testResult, STR ("Processor and edit controller united.")); + return true; + } + + FUID controllerClassUID; + if (vstPlug->getControllerClassId (controllerClassUID) != kResultOk) + { + addMessage (testResult, STR ("This component does not export an edit controller class ID!!!")); + return true; + } + if (controllerClassUID.isValid () == false) + { + addErrorMessage (testResult, STR ("The edit controller class has no valid UID!!!")); + return false; + } + + addMessage (testResult, STR ("This component has an edit controller class")); + + char8 cidString[50]; + + controllerClassUID.toRegistryString (cidString); + addMessage (testResult, printf (" Controller CID: %s", cidString)); + + return true; +} + + +//------------------------------------------------------------------------ +// VstUnitInfoTest +//------------------------------------------------------------------------ +VstUnitInfoTest::VstUnitInfoTest (IPlugProvider* plugProvider) +: VstTestBase (plugProvider) +{ +} + +//------------------------------------------------------------------------ +bool VstUnitInfoTest::run (ITestResult* testResult) +{ + if (!vstPlug || !testResult) + return false; + + printTestHeader (this, testResult); + + FUnknownPtr iUnitInfo (controller); + if (iUnitInfo) + { + int32 unitCount = iUnitInfo->getUnitCount (); + if (unitCount <= 0) + { + addMessage (testResult, STR ("No units found, while controller implements IUnitInfo !!!")); + } + else + { + addMessage (testResult, printf ("This component has %d unit(s).", unitCount)); + } + + int32* unitIds = new int32[unitCount]; + ArrayDeleter unitIdDeleter (unitIds); + + for (int32 unitIndex = 0; unitIndex < unitCount; unitIndex++) + { + UnitInfo unitInfo = {0}; + + if (iUnitInfo->getUnitInfo (unitIndex, unitInfo) == kResultOk) + { + int32 unitId = unitInfo.id; + unitIds[unitIndex] = unitId; + if (unitId < 0) + { + addErrorMessage (testResult, printf ("Unit %03d: Invalid ID!", unitIndex)); + return false; + } + + // check if ID is already used by another unit + for (int32 idIndex = 0; idIndex < unitIndex; idIndex++) + { + if (unitIds[idIndex] == unitIds[unitIndex]) + { + addErrorMessage (testResult, printf ("Unit %03d: ID already used!!!", unitIndex)); + return false; + } + } + + auto unitName = VST3::StringConvert::convert (unitInfo.name); + if (unitName.empty ()) + { + addErrorMessage (testResult, printf ("Unit %03d: No name!", unitIndex)); + return false; + } + + int32 parentUnitId = unitInfo.parentUnitId; + if (parentUnitId < -1) + { + addErrorMessage (testResult, printf ("Unit %03d: Invalid parent ID!", unitIndex)); + return false; + } + else if (parentUnitId == unitId) + { + addErrorMessage (testResult, printf ("Unit %03d: Parent ID is equal to Unit ID!", unitIndex)); + return false; + } + + int32 unitProgramListId = unitInfo.programListId; + if (unitProgramListId < -1) + { + addErrorMessage (testResult, printf ("Unit %03d: Invalid programlist ID!", unitIndex)); + return false; + } + + addMessage (testResult, printf (" Unit%03d (ID = %d): \"%s\" (parent ID = %d, programlist ID = %d)", + unitIndex, unitId, unitName.data (), parentUnitId, unitProgramListId)); + + // test select Unit + if (iUnitInfo->selectUnit (unitIndex) == kResultTrue) + { + UnitID newSelected = iUnitInfo->getSelectedUnit (); + if (newSelected != unitIndex) + { + addMessage (testResult, printf ("The host has selected Unit ID = %d but getSelectedUnit returns ID = %d!!!", + unitIndex, newSelected)); + } + } + } + else + { + addMessage (testResult, printf ("Unit%03d: No unit info!", unitIndex)); + } + } + } + else + { + addMessage (testResult, STR ("This component has no units.")); + } + + return true; +} + + +//------------------------------------------------------------------------ +// VstUnitStructureTest +//------------------------------------------------------------------------ +VstUnitStructureTest::VstUnitStructureTest (IPlugProvider* plugProvider) +: VstTestBase (plugProvider) +{ +} + +//------------------------------------------------------------------------ +bool VstUnitStructureTest::run (ITestResult* testResult) +{ + if (!vstPlug || !testResult) + return false; + + printTestHeader (this, testResult); + + FUnknownPtr iUnitInfo (controller); + if (iUnitInfo) + { + int32 unitCount = iUnitInfo->getUnitCount (); + if (unitCount <= 0) + { + addMessage (testResult, STR ("No units found, while controller implements IUnitInfo !!!")); + } + + UnitInfo unitInfo = {0}; + UnitInfo tmpInfo = {0}; + bool rootFound = false; + for (int32 unitIndex = 0; unitIndex < unitCount; unitIndex++) + { + if (iUnitInfo->getUnitInfo (unitIndex, unitInfo) == kResultOk) + { + // check parent Id + if (unitInfo.parentUnitId != kNoParentUnitId) //-1: connected to root + { + bool noParent = true; + for (int32 i = 0; i < unitCount; ++i) + { + if (iUnitInfo->getUnitInfo (i, tmpInfo) == kResultOk) + { + if (unitInfo.parentUnitId == tmpInfo.id) + { + noParent = false; + break; + } + } + } + if (noParent && unitInfo.parentUnitId != kRootUnitId) + { + addErrorMessage (testResult, printf ("Unit %03d: Parent does not exist!!", unitInfo.id)); + return false; + } + } + else if (!rootFound) + { + // root Unit have always the rootID + if (unitInfo.id != kRootUnitId) + { + // we should have a root unit id + addErrorMessage (testResult, printf ("Unit %03d: Should be the Root Unit => id should be %03d!!", unitInfo.id, kRootUnitId)); + return false; + } + rootFound = true; + } + else + { + addErrorMessage (testResult, printf ("Unit %03d: Has no parent, but there is a root already.", unitInfo.id)); + return false; + } + } + else + { + addErrorMessage (testResult, printf ("Unit %03d: No unit info.", unitInfo.id)); + return false; + } + } + addMessage (testResult, STR ("All units have valid parent IDs.")); + } + else + { + addMessage (testResult, STR ("This component does not support IUnitInfo!")); + } + return true; +} + + +//------------------------------------------------------------------------ +// VstProgramInfoTest +//------------------------------------------------------------------------ +VstProgramInfoTest::VstProgramInfoTest (IPlugProvider* plugProvider) +: VstTestBase (plugProvider) +{ +} + +//------------------------------------------------------------------------ +bool VstProgramInfoTest::run (ITestResult* testResult) +{ + if (!vstPlug || !testResult) + return false; + + printTestHeader (this, testResult); + + FUnknownPtr iUnitInfo (controller); + if (iUnitInfo) + { + int32 programListCount = iUnitInfo->getProgramListCount (); + if (programListCount == 0) + { + addMessage (testResult, STR ("This component does not export any programs.")); + return true; + } + else if (programListCount < 0) + { + addErrorMessage (testResult, STR ("IUnitInfo::getProgramListCount () returned a negative number.")); + return false; + } + + // used to check double IDs + int32* programListIds = new int32 [programListCount]; + ArrayDeleter idDeleter (programListIds); + + for (int32 programListIndex = 0; programListIndex < programListCount; programListIndex++) + { + // get programm list info + ProgramListInfo programListInfo; + if (iUnitInfo->getProgramListInfo (programListIndex, programListInfo) == kResultOk) + { + int32 programListId = programListInfo.id; + programListIds [programListIndex] = programListId; + if (programListId < 0) + { + addErrorMessage (testResult, printf ("Programlist%03d: Invalid ID!!!", programListIndex)); + return false; + } + + // check if ID is already used by another parameter + for (int32 idIndex = 0; idIndex < programListIndex; idIndex++) + { + if (programListIds[idIndex] == programListIds[programListIndex]) + { + addErrorMessage (testResult, printf ("Programlist%03d: ID already used!!!", programListIndex)); + return false; + } + } + + auto programListName = VST3::StringConvert::convert (programListInfo.name); + if (programListName.empty ()) + { + addErrorMessage (testResult, printf ("Programlist%03d (ID = %d): No name!!!", programListIndex, programListId)); + return false; + } + + int32 programCount = programListInfo.programCount; + if (programCount <= 0) + { + addMessage (testResult, printf ("Programlist%03d (ID = %d): \"%s\" No programs!!! (programCount is null!)", programListIndex, programListId, VST3::StringConvert::convert (programListName).data ())); + //return false; + } + + addMessage (testResult, printf ("Programlist%03d (ID = %d): \"%s\" (%d programs).", programListIndex, programListId, programListName.data (), programCount)); + + for (int32 programIndex = 0; programIndex < programCount; programIndex++) + { + TChar programName[256]; + if (iUnitInfo->getProgramName (programListId, programIndex, programName) == kResultOk) + { + if (programName[0] == 0) + { + addErrorMessage (testResult, printf ("Programlist%03d-Program%03d: has no name!!!", programListIndex, programIndex)); + return false; + } + + auto programNameUTF8 = VST3::StringConvert::convert (programName); + auto msg = printf ("Programlist%03d-Program%03d: \"%s\"", programListIndex, programIndex, programNameUTF8.data ()); + + String128 programInfo {}; + if (iUnitInfo->getProgramInfo (programListId, programIndex, PresetAttributes::kInstrument, programInfo) == kResultOk) + { + auto programInfoUTF8 = VST3::StringConvert::convert (programInfo); + msg += VST3::StringConvert::convert (" (instrument = \""); + msg += (const char16_t*)programInfo; + msg += VST3::StringConvert::convert ("\")"); + } + + addMessage (testResult, msg.data ()); + + if (iUnitInfo->hasProgramPitchNames (programListId, programIndex) == kResultOk) + { + addMessage (testResult, printf (" => \"%s\": supports PitchNames", programNameUTF8.data ())); + + String128 pitchName = {0}; + for (int16 midiPitch = 0; midiPitch < 128; midiPitch++) + { + if (iUnitInfo->getProgramPitchName (programListId, programIndex, midiPitch, pitchName) == kResultOk) + { + msg = printf (" => MIDI Pitch %d => \"", midiPitch); + msg += (const char16_t*)pitchName; + msg += VST3::StringConvert::convert ("\""); + addMessage (testResult, msg.data ()); + } + } + } + } + } + } + } + } + else + addMessage (testResult, STR ("This component does not export any programs.")); + + return true; +} + +//------------------------------------------------------------------------ +// VstValidStateTransitionTest +//------------------------------------------------------------------------ +VstValidStateTransitionTest::VstValidStateTransitionTest (IPlugProvider* plugProvider) +: VstTestBase (plugProvider) +{} + +//------------------------------------------------------------------------ +bool PLUGIN_API VstValidStateTransitionTest::run (ITestResult* testResult) +{ + if (!vstPlug || !testResult) + return false; + + printTestHeader (this, testResult); + + for (int32 i = 0; i < 3; ++i) + { + tresult result = vstPlug->setActive (true); + if (result != kResultTrue) + return false; + + result = vstPlug->setActive (false); + if (result != kResultTrue) + return false; + + result = vstPlug->terminate (); + if (result != kResultTrue) + return false; + + result = vstPlug->initialize (gStandardPluginContext); + if (result != kResultTrue) + return false; + } + return true; +} + +//------------------------------------------------------------------------ +// VstInvalidStateTransitionTest +//------------------------------------------------------------------------ +VstInvalidStateTransitionTest::VstInvalidStateTransitionTest (IPlugProvider* plugProvider) +: VstTestBase (plugProvider) +{} + +//------------------------------------------------------------------------ +bool PLUGIN_API VstInvalidStateTransitionTest::run (ITestResult* testResult) +{ + if (!vstPlug || !testResult) + return false; + + printTestHeader (this, testResult); + + // created + tresult result = vstPlug->initialize (gStandardPluginContext); + if (result == kResultFalse) + return false; + + // initialized + result = vstPlug->setActive (false); + if (result == kResultOk) + return false; + + result = vstPlug->setActive (true); + if (result == kResultFalse) + return false; + + // allocated + result = vstPlug->initialize (gStandardPluginContext); + if (result == kResultOk) + return false; + + result = vstPlug->setActive (false); + if (result == kResultFalse) + return false; + + // deallocated (initialized) + result = vstPlug->initialize (gStandardPluginContext); + if (result == kResultOk) + return false; + + result = vstPlug->terminate (); + if (result == kResultFalse) + return false; + + // terminated (created) + result = vstPlug->setActive (false); + if (result == kResultOk) + return false; + + result = vstPlug->terminate (); + if (result == kResultOk) + return false; + + return true; +} + +//------------------------------------------------------------------------ +// VstRepeatIdenticalStateTransitionTest +//------------------------------------------------------------------------ +VstRepeatIdenticalStateTransitionTest::VstRepeatIdenticalStateTransitionTest (IPlugProvider* plugProvider) +: VstTestBase (plugProvider) +{} + +//------------------------------------------------------------------------ +bool VstRepeatIdenticalStateTransitionTest::run (ITestResult* testResult) +{ + if (!vstPlug || !testResult) + return false; + + printTestHeader (this, testResult); + + tresult result = vstPlug->initialize (gStandardPluginContext); + if (result != kResultFalse) + return false; + + result = vstPlug->setActive (true); + if (result != kResultOk) + return false; + + result = vstPlug->setActive (true); + if (result != kResultFalse) + return false; + + result = vstPlug->setActive (false); + if (result != kResultOk) + return false; + + result = vstPlug->setActive (false); + if (result == kResultOk) + return false; + + result = vstPlug->terminate (); + if (result != kResultOk) + return false; + + result = vstPlug->terminate (); + if (result == kResultOk) + return false; + + result = vstPlug->initialize (gStandardPluginContext); + if (result != kResultOk) + return false; + + return true; +} + +//------------------------------------------------------------------------ +// VstBusConsistencyTest +//------------------------------------------------------------------------ +VstBusConsistencyTest::VstBusConsistencyTest (IPlugProvider* plugProvider) +: VstTestBase (plugProvider) +{ +} + +//------------------------------------------------------------------------ +bool PLUGIN_API VstBusConsistencyTest::run (ITestResult* testResult) +{ + if (!vstPlug || !testResult) + return false; + + printTestHeader (this, testResult); + + bool failed = false; + int32 numFalseDescQueries = 0; + + for (MediaType mediaType = kAudio; mediaType < kNumMediaTypes; mediaType++) + { + for (BusDirection dir = kInput; dir <= kOutput; dir++) + { + int32 numBusses = vstPlug->getBusCount (mediaType, dir); + if (numBusses > 0) + { + BusInfo* busArray = new BusInfo[numBusses]; + if (busArray) + { + // get all bus descriptions and save them in an array + int32 busIndex; + for (busIndex = 0; busIndex < numBusses; busIndex++) + { + memset (&busArray[busIndex], 0, sizeof (BusInfo)); + vstPlug->getBusInfo (mediaType, dir, busIndex, busArray[busIndex]); + } + + // test by getting descriptions randomly and comparing with saved ones + int32 randIndex = 0; + BusInfo info = {0}; + + for (busIndex = 0; busIndex <= numBusses * NUM_ITERATIONS; busIndex++) + { + randIndex = rand () % (numBusses); + + memset (&info, 0, sizeof (BusInfo)); + + /*tresult result =*/ vstPlug->getBusInfo (mediaType, dir, randIndex, info); + if (memcmp ((void*)&busArray[randIndex], (void*)&info, sizeof (BusInfo)) != BUFFERS_ARE_EQUAL) + { + failed |= true; + numFalseDescQueries++; + } + } + delete [] busArray; + } + } + } + } + + if (numFalseDescQueries > 0) + { + addErrorMessage (testResult, printf ("The component returned %i inconsistent buses! (getBusInfo () returns sometime different info for the same bus!", numFalseDescQueries)); + } + + return failed == false; +} + +//------------------------------------------------------------------------ +// VstBusInvalidIndexTest +//------------------------------------------------------------------------ +VstBusInvalidIndexTest::VstBusInvalidIndexTest (IPlugProvider* plugProvider) +: VstTestBase (plugProvider) +{ +} + +//------------------------------------------------------------------------ +bool PLUGIN_API VstBusInvalidIndexTest::run (ITestResult* testResult) +{ + if (!vstPlug || !testResult) + return false; + + printTestHeader (this, testResult); + + bool failed = false; + int32 numInvalidDesc = 0; + + for (MediaType mediaType = kAudio; mediaType < kNumMediaTypes; mediaType++) + { + int32 numBusses = vstPlug->getBusCount (mediaType, kInput) + vstPlug->getBusCount (mediaType, kOutput); + for (BusDirection dir = kInput; dir <= kOutput; dir++) + { + BusInfo descBefore = {0}; + BusInfo descAfter = {0}; + + int32 randIndex = 0; + + // todo: rand with negative numbers + for (int32 i = 0; i <= numBusses * NUM_ITERATIONS; ++i) + { + randIndex = rand (); + if (0 > randIndex || randIndex > numBusses) + { + /*tresult result =*/ vstPlug->getBusInfo (mediaType, dir, randIndex, descAfter); + + if (memcmp ((void*)&descBefore, (void*)&descAfter, sizeof (BusInfo)) != 0) + { + failed |= true; + numInvalidDesc++; + } + } + } + } + } + + if (numInvalidDesc > 0) + { + addErrorMessage (testResult, printf ("The component returned %i buses queried with an invalid index!", numInvalidDesc)); + } + + return failed == false; +} + +//------------------------------------------------------------------------ +// VstProcessTest +//------------------------------------------------------------------------ +VstProcessTest::VstProcessTest (IPlugProvider* plugProvider, ProcessSampleSize sampl) +: VstTestEnh (plugProvider, sampl) +{ + processData.numSamples = DEFAULT_BLOCK_SIZE; + processData.symbolicSampleSize = sampl; + + processSetup.processMode = kRealtime; + processSetup.symbolicSampleSize = sampl; + processSetup.maxSamplesPerBlock = MAX_BLOCK_SIZE; + processSetup.sampleRate = DEFAULT_SAMPLE_RATE; +} + +//------------------------------------------------------------------------ +bool PLUGIN_API VstProcessTest::setup () +{ + if (!VstTestEnh::setup ()) + return false; + if (!vstPlug || !audioEffect) + return false; + if (processSetup.symbolicSampleSize != processData.symbolicSampleSize) + return false; + if (audioEffect->canProcessSampleSize (processSetup.symbolicSampleSize) != kResultOk) + return true; // this fails in run (..) + + prepareProcessing (); + + if (vstPlug->setActive (true) != kResultTrue) + return false; + return true; +} + +//------------------------------------------------------------------------ +bool PLUGIN_API VstProcessTest::run (ITestResult* testResult) +{ + if (!testResult || !audioEffect) + return false; + if (processSetup.symbolicSampleSize != processData.symbolicSampleSize) + return false; + if (!canProcessSampleSize (testResult)) + return true; + + audioEffect->setProcessing (true); + + for (int32 i = 0; i < NUM_AUDIO_BLOCKS_TO_PROCESS; ++i) + { + if (!preProcess (testResult)) + return false; + tresult result = audioEffect->process (processData); + if (result != kResultOk) + { + addErrorMessage (testResult, STR ("IAudioProcessor::process (..) failed.")); + + audioEffect->setProcessing (false); + return false; + } + if (!postProcess (testResult)) + { + audioEffect->setProcessing (false); + return false; + } + } + + audioEffect->setProcessing (false); + return true; +} + +//------------------------------------------------------------------------ +bool VstProcessTest::preProcess (ITestResult* testResult) +{ + return true; +} + +//------------------------------------------------------------------------ +bool VstProcessTest::postProcess (ITestResult* testResult) +{ + return true; +} + +//------------------------------------------------------------------------ +bool VstProcessTest::canProcessSampleSize (ITestResult* testResult) +{ + if (!testResult || !audioEffect) + return false; + if (processSetup.symbolicSampleSize != processData.symbolicSampleSize) + return false; + if (audioEffect->canProcessSampleSize (processSetup.symbolicSampleSize) != kResultOk) + { + if (processSetup.symbolicSampleSize == kSample32) + addMessage (testResult, STR ("32bit Audio Processing not supported.")); + else + addMessage (testResult, STR ("64bit Audio Processing not supported.")); + return false; + } + return true; +} + +//------------------------------------------------------------------------ +bool PLUGIN_API VstProcessTest::teardown () +{ + unprepareProcessing (); + if (!vstPlug || (vstPlug->setActive (false) != kResultOk)) + return false; + return VstTestEnh::teardown (); +} + +//------------------------------------------------------------------------ +bool VstProcessTest::prepareProcessing () +{ + if (!vstPlug || !audioEffect) + return false; + + if (audioEffect->setupProcessing (processSetup) == kResultOk) + { + processData.prepare (*vstPlug, 0, processSetup.symbolicSampleSize); + + for (BusDirection dir = kInput; dir <= kOutput; dir++) + { + int32 numBusses = vstPlug->getBusCount (kAudio, dir); + AudioBusBuffers* audioBuffers = dir == kInput ? processData.inputs : processData.outputs; //new AudioBusBuffers [numBusses]; + if (!setupBuffers (numBusses, audioBuffers, dir)) + return false; + + if (dir == kInput) + { + processData.numInputs = numBusses; + processData.inputs = audioBuffers; + } + else + { + processData.numOutputs = numBusses; + processData.outputs = audioBuffers; + } + } + return true; + } + return false; +} + +//------------------------------------------------------------------------ +bool VstProcessTest::setupBuffers (int32 numBusses, AudioBusBuffers* audioBuffers, BusDirection dir) +{ + if (((numBusses > 0) && !audioBuffers) || !vstPlug) + return false; + for (int32 busIndex = 0; busIndex < numBusses; busIndex++) // buses + { + BusInfo busInfo; + if (vstPlug->getBusInfo (kAudio, dir, busIndex, busInfo) == kResultTrue) + { + if (!setupBuffers (audioBuffers[busIndex])) + return false; + + if ((busInfo.flags & BusInfo::kDefaultActive) != 0) + { + for (int32 channelIndex = 0; channelIndex < busInfo.channelCount; channelIndex++) // channels per bus + audioBuffers[busIndex].silenceFlags |= (uint64)CHANNEL_IS_SILENT << channelIndex; + } + } + else + return false; + } + return true; +} + +//------------------------------------------------------------------------ +bool VstProcessTest::setupBuffers (AudioBusBuffers& audioBuffers) +{ + if (processSetup.symbolicSampleSize != processData.symbolicSampleSize) + return false; + audioBuffers.silenceFlags = 0; + for (int32 channelIndex = 0; channelIndex < audioBuffers.numChannels; channelIndex++) + { + if (processSetup.symbolicSampleSize == kSample32) + { + if (audioBuffers.channelBuffers32) + { + audioBuffers.channelBuffers32[channelIndex] = new Sample32[processSetup.maxSamplesPerBlock]; + if (audioBuffers.channelBuffers32[channelIndex]) + memset (audioBuffers.channelBuffers32[channelIndex], 0, processSetup.maxSamplesPerBlock * sizeof (Sample32)); + else + return false; + } + else + return false; + } + else if (processSetup.symbolicSampleSize == kSample64) + { + if (audioBuffers.channelBuffers64) + { + audioBuffers.channelBuffers64[channelIndex] = new Sample64[processSetup.maxSamplesPerBlock]; + if (audioBuffers.channelBuffers64[channelIndex]) + memset (audioBuffers.channelBuffers64[channelIndex], 0, processSetup.maxSamplesPerBlock * sizeof (Sample64)); + else + return false; + } + else + return false; + } + else + return false; + } + return true; +} + +//------------------------------------------------------------------------ +bool VstProcessTest::unprepareProcessing () +{ + bool ret = true; + ret &= freeBuffers (processData.numInputs, processData.inputs); + ret &= freeBuffers (processData.numOutputs, processData.outputs); + processData.unprepare (); + return ret; +} + +//------------------------------------------------------------------------ +bool VstProcessTest::freeBuffers (int32 numBuses, AudioBusBuffers* buses) +{ + if (processSetup.symbolicSampleSize != processData.symbolicSampleSize) + return false; + for (int32 busIndex = 0; busIndex < numBuses; busIndex++) + { + for (int32 channelIndex = 0; channelIndex < buses[busIndex].numChannels; channelIndex++) + { + if (processSetup.symbolicSampleSize == kSample32) + delete [] buses[busIndex].channelBuffers32[channelIndex]; + else if (processSetup.symbolicSampleSize == kSample64) + delete [] buses[busIndex].channelBuffers64[channelIndex]; + else + return false; + } + } + return true; +} + + +//------------------------------------------------------------------------ +// VstSpeakerArrangementTest +//------------------------------------------------------------------------ +VstSpeakerArrangementTest::VstSpeakerArrangementTest (IPlugProvider* plugProvider, ProcessSampleSize sampl, + SpeakerArrangement inSpArr, SpeakerArrangement outSpArr) +: VstProcessTest (plugProvider, sampl) +, inSpArr (inSpArr) +, outSpArr (outSpArr) +{ +} + +//------------------------------------------------------------------------ +const char* VstSpeakerArrangementTest::getSpeakerArrangementName (SpeakerArrangement spArr) +{ + const char* saName = nullptr; + switch (spArr) + { + case SpeakerArr::kMono: saName = "Mono"; break; + case SpeakerArr::kStereo: saName = "Stereo"; break; + case SpeakerArr::kStereoSurround: saName = "StereoSurround"; break; + case SpeakerArr::kStereoCenter: saName = "StereoCenter"; break; + case SpeakerArr::kStereoSide: saName = "StereoSide"; break; + case SpeakerArr::kStereoCLfe: saName = "StereoCLfe"; break; + case SpeakerArr::k30Cine: saName = "30Cine"; break; + case SpeakerArr::k30Music: saName = "30Music"; break; + case SpeakerArr::k31Cine: saName = "31Cine"; break; + case SpeakerArr::k31Music: saName = "31Music"; break; + case SpeakerArr::k40Cine: saName = "40Cine"; break; + case SpeakerArr::k40Music: saName = "40Music"; break; + case SpeakerArr::k41Cine: saName = "41Cine"; break; + case SpeakerArr::k41Music: saName = "41Music"; break; + case SpeakerArr::k50: saName = "50"; break; + case SpeakerArr::k51: saName = "51"; break; + case SpeakerArr::k60Cine: saName = "60Cine"; break; + case SpeakerArr::k60Music: saName = "60Music"; break; + case SpeakerArr::k61Cine: saName = "61Cine"; break; + case SpeakerArr::k61Music: saName = "61Music"; break; + case SpeakerArr::k70Cine: saName = "70Cine"; break; + case SpeakerArr::k70Music: saName = "70Music"; break; + case SpeakerArr::k71Cine: saName = "71Cine"; break; + case SpeakerArr::k71Music: saName = "71Music"; break; + case SpeakerArr::k80Cine: saName = "80Cine"; break; + case SpeakerArr::k80Music: saName = "80Music"; break; + case SpeakerArr::k81Cine: saName = "81Cine"; break; + case SpeakerArr::k81Music: saName = "81Music"; break; + case SpeakerArr::k102: saName = "102"; break; + case SpeakerArr::k122: saName = "122"; break; + case SpeakerArr::k80Cube: saName = "80Cube"; break; + case SpeakerArr::kBFormat: saName = "BFormat"; break; + case SpeakerArr::k90: saName = "9.0"; break; + case SpeakerArr::k91: saName = "9.1"; break; + case SpeakerArr::k100: saName = "10.0"; break; + case SpeakerArr::k101: saName = "10.1"; break; + case SpeakerArr::k110: saName = "11.0"; break; + case SpeakerArr::k111: saName = "11.1"; break; + case SpeakerArr::k130: saName = "13.0"; break; + case SpeakerArr::k131: saName = "13.1"; break; + case SpeakerArr::kEmpty: saName = "Empty"; break; + default: saName = "Unknown"; break; + } + return saName; +} + +//------------------------------------------------------------------------ +const char* VstSpeakerArrangementTest::getName () const +{ + const auto inSaName = getSpeakerArrangementName (inSpArr); + const auto outSaName = getSpeakerArrangementName (outSpArr); + if (inSaName && outSaName) + { + static std::string str; + str = "In: "; + str += inSaName; + str += ": "; + str += std::to_string (SpeakerArr::getChannelCount (inSpArr)); + str += " Channels, Out: "; + str += outSaName; + str += ": "; + str += std::to_string(SpeakerArr::getChannelCount (outSpArr)); + str += " Channels"; + return str.data (); + } + return "error"; +} + +//------------------------------------------------------------------------ +bool VstSpeakerArrangementTest::prepareProcessing () +{ + if (!vstPlug || !audioEffect) + return false; + + bool ret = true; + int32 is = vstPlug->getBusCount (kAudio, kInput); + SpeakerArrangement* inSpArrs = new SpeakerArrangement[is]; + for (int32 i = 0; i < is; ++i) + inSpArrs[i] = inSpArr; + + int32 os = vstPlug->getBusCount (kAudio, kOutput); + SpeakerArrangement* outSpArrs = new SpeakerArrangement[os]; + for (int32 o = 0; o < os; o++) + outSpArrs[o] = outSpArr; + + if (audioEffect->setBusArrangements (inSpArrs, is, outSpArrs, os) != kResultTrue) + ret = false; + + ret &= VstProcessTest::prepareProcessing (); + + delete [] inSpArrs; + delete [] outSpArrs; + + return ret; +} + +//------------------------------------------------------------------------ +bool VstSpeakerArrangementTest::run (ITestResult* testResult) +{ + if (!testResult || !audioEffect || !vstPlug) + return false; + + printTestHeader (this, testResult); + + SpeakerArrangement spArr = SpeakerArr::kEmpty; + SpeakerArrangement compareSpArr = SpeakerArr::kEmpty; + BusDirections bd = kInput; + BusInfo busInfo = {0}; + int32 count = 0; + do + { + count++; + int32 numBusses = 0; + if (bd == kInput) + { + numBusses = processData.numInputs; + compareSpArr = inSpArr; + } + else + { + numBusses = processData.numOutputs; + compareSpArr = outSpArr; + } + for (int32 i = 0; i < numBusses; ++i) + { + if (audioEffect->getBusArrangement (bd, i, spArr) != kResultTrue) + { + addErrorMessage (testResult, STR ("IAudioProcessor::getBusArrangement (..) failed.")); + return false; + } + if (spArr != compareSpArr) + { + addMessage (testResult, printf (" %s %sSpeakerArrangement is not supported. Plug-in suggests: %s.", + getSpeakerArrangementName (compareSpArr), + bd == kInput ? "Input-" : "Output-", + getSpeakerArrangementName (spArr))); + } + if (vstPlug->getBusInfo (kAudio, bd, i, busInfo) != kResultTrue) + { + addErrorMessage (testResult, STR ("IComponent::getBusInfo (..) failed.")); + return false; + } + if (spArr == compareSpArr && SpeakerArr::getChannelCount (spArr) != busInfo.channelCount) + { + addErrorMessage (testResult, STR ("SpeakerArrangement mismatch (BusInfo::channelCount inconsistency).")); + return false; + } + } + bd = kOutput; + } while (count < 2); + + bool ret = true; + + // not a Pb ret &= verifySA (processData.numInputs, processData.inputs, inSpArr, testResult); + // not a Pb ret &= verifySA (processData.numOutputs, processData.outputs, outSpArr, testResult); + ret &= VstProcessTest::run (testResult); + + return ret; +} + +//------------------------------------------------------------------------ +bool VstSpeakerArrangementTest::verifySA (int32 numBusses, AudioBusBuffers* buses, SpeakerArrangement spArr, ITestResult* testResult) +{ + if (!testResult || !buses) + return false; + for (int32 i = 0; i < numBusses; ++i) + { + if (buses[i].numChannels != SpeakerArr::getChannelCount (spArr)) + { + addErrorMessage (testResult, STR ("ChannelCount is not matching SpeakerArrangement.")); + return false; + } + } + return true; +} + +IMPLEMENT_FUNKNOWN_METHODS (ParamChanges, IParamValueQueue, IParamValueQueue::iid) + +//------------------------------------------------------------------------ +// VstAutomationTest +//------------------------------------------------------------------------ +IMPLEMENT_FUNKNOWN_METHODS (VstAutomationTest, IParameterChanges, IParameterChanges::iid) + +//------------------------------------------------------------------------ +VstAutomationTest::VstAutomationTest (IPlugProvider* plugProvider, ProcessSampleSize sampl, + int32 everyNSamples, int32 numParams, bool sampleAccuracy) +: VstProcessTest (plugProvider, sampl) +, bypassId (-1) +, paramChanges (nullptr) +, countParamChanges (0) +, everyNSamples (everyNSamples) +, numParams (numParams) +, sampleAccuracy (sampleAccuracy) +, onceExecuted (false) +{ + FUNKNOWN_CTOR +} + +//------------------------------------------------------------------------ +VstAutomationTest::~VstAutomationTest () +{ + FUNKNOWN_DTOR +} + +//------------------------------------------------------------------------ +const char* VstAutomationTest::getName () const +{ + static std::string text; + const char* accTxt = "Sample"; + if (!sampleAccuracy) + accTxt = "Block"; + text = "Accuracy: "; + text += accTxt; + if (numParams < 1) + text += ", All Parameters"; + else + { + text += ", "; + text += std::to_string (numParams); + text += " Parameters"; + } + text += ", Change every"; + text += std::to_string (everyNSamples); + text += " Samples"; + return text.data (); +} + +//------------------------------------------------------------------------ +bool VstAutomationTest::setup () +{ + onceExecuted = false; + if (!VstProcessTest::setup ()) + return false; + + if (!controller) + return false; + if ((numParams < 1) || (numParams > controller->getParameterCount ())) + numParams = controller->getParameterCount (); + if (audioEffect && (numParams > 0)) + { + paramChanges = new ParamChanges [numParams]; + ParameterInfo inf = {0}; + for (int32 i = 0; i < numParams; ++i) + { + tresult r = controller->getParameterInfo (i, inf); + if (r != kResultTrue) + return false; + if ((inf.flags & inf.kCanAutomate) != 0) + paramChanges[i].init (inf.id, processSetup.maxSamplesPerBlock); + } + + for (int32 i = 0; i < controller->getParameterCount (); ++i) + { + tresult r = controller->getParameterInfo (i, inf); + if (r != kResultTrue) + return false; + if ((inf.flags & inf.kIsBypass) != 0) + { + bypassId = inf.id; + break; + } + } + return true; + } + return numParams == 0; +} + +//------------------------------------------------------------------------ +bool VstAutomationTest::run (ITestResult* testResult) +{ + printTestHeader (this, testResult); + + if (!testResult) + return false; + if (numParams == 0) + addMessage (testResult, STR ("No Parameters present.")); + bool ret = VstProcessTest::run (testResult); + return ret; +} + +//------------------------------------------------------------------------ +bool VstAutomationTest::teardown () +{ + if (paramChanges) + { + delete [] paramChanges; + paramChanges = 0; + } + return VstProcessTest::teardown (); +} + +//------------------------------------------------------------------------ +bool VstAutomationTest::preProcess (ITestResult* testResult) +{ + if (!testResult) + return false; + if (!paramChanges) + return numParams == 0; + bool check = true; + for (int32 i = 0; i < numParams; ++i) + { + paramChanges[i].resetPoints (); + int32 point = 0; + for (int32 pos = 0; pos < processData.numSamples; pos++) + { + bool add = (rand () % everyNSamples) == 0; + if (!onceExecuted) + { + if (pos == 0) + { + add = true; + if (!sampleAccuracy) + onceExecuted = true; + } + else if ((pos == 1) && sampleAccuracy) + { + add = true; + onceExecuted = true; + } + } + if (add) + check &= paramChanges[i].setPoint (point++, pos, ((float)(rand () % 1000000000)) / 1000000000.0); + } + if (check) + processData.inputParameterChanges = this; + } + return check; +} + +//------------------------------------------------------------------------ +bool VstAutomationTest::postProcess (ITestResult* testResult) +{ + if (!testResult) + return false; + if (!paramChanges) + return numParams == 0; + + for (int32 i = 0; i < numParams; ++i) + { + if ((paramChanges[i].getPointCount () > 0) && !paramChanges[i].havePointsBeenRead (!sampleAccuracy)) + { + if (sampleAccuracy) + addMessage (testResult, STR (" Not all points have been read via IParameterChanges")); + else + addMessage (testResult, STR (" No point at all has been read via IParameterChanges")); + + return true;// should not be a problem + } + } + return true; +} + +//------------------------------------------------------------------------ +int32 VstAutomationTest::getParameterCount () +{ + if (paramChanges) + return numParams; + return 0; +} + +//------------------------------------------------------------------------ +IParamValueQueue* VstAutomationTest::getParameterData (int32 index) +{ + if (paramChanges && (index >= 0) && (index < getParameterCount ())) + return ¶mChanges[index]; + return 0; +} + +//------------------------------------------------------------------------ +IParamValueQueue* VstAutomationTest::addParameterData (const ParamID& id, int32& index) +{ + return 0; +} + +//------------------------------------------------------------------------ +// VstFlushParamTest +//------------------------------------------------------------------------ +VstFlushParamTest::VstFlushParamTest (IPlugProvider* plugProvider, ProcessSampleSize sampl) +: VstAutomationTest (plugProvider, sampl, 100, 1, false) +{ +} + +//------------------------------------------------------------------------ +bool PLUGIN_API VstFlushParamTest::run (ITestResult* testResult) +{ + if (!vstPlug || !testResult || !audioEffect) + return false; + if (!canProcessSampleSize (testResult)) + return true; + + printTestHeader (this, testResult); + + unprepareProcessing (); + + processData.numSamples = 0; + processData.numInputs = 0; + processData.numOutputs = 0; + processData.inputs = nullptr; + processData.outputs = nullptr; + + audioEffect->setProcessing (true); + + preProcess (testResult); + + tresult result = audioEffect->process (processData); + if (result != kResultOk) + { + addErrorMessage (testResult, STR ("The component failed to process without audio buffers!")); + + audioEffect->setProcessing (false); + return false; + } + + postProcess (testResult); + + audioEffect->setProcessing (false); + return true; +} + + +//------------------------------------------------------------------------ +// VstSilenceFlagsTest +//------------------------------------------------------------------------ +VstSilenceFlagsTest::VstSilenceFlagsTest (IPlugProvider* plugProvider, ProcessSampleSize sampl) +: VstProcessTest (plugProvider, sampl) +{ +} + +//------------------------------------------------------------------------ +bool PLUGIN_API VstSilenceFlagsTest::run (ITestResult* testResult) +{ + if (!vstPlug || !testResult || !audioEffect) + return false; + if (!canProcessSampleSize (testResult)) + return true; + + printTestHeader (this, testResult); + + if (processData.inputs != 0) + { + audioEffect->setProcessing (true); + + for (int32 inputsIndex = 0; inputsIndex < processData.numInputs; inputsIndex++) + { + int32 numSilenceFlagsCombinations = (1 << processData.inputs[inputsIndex].numChannels) - 1; + for (int32 flagCombination = 0; flagCombination <= numSilenceFlagsCombinations; flagCombination++) + { + processData.inputs[inputsIndex].silenceFlags = flagCombination; + tresult result = audioEffect->process (processData); + if (result != kResultOk) + { + addErrorMessage (testResult, printf ("The component failed to process bus %i with silence flag combination %x!", inputsIndex, flagCombination)); + audioEffect->setProcessing (false); + return false; + } + } + } + } + else if (processData.numInputs > 0) + { + addErrorMessage (testResult, STR ("ProcessData::inputs are 0 but ProcessData::numInputs are nonzero.")); + return false; + } + + audioEffect->setProcessing (false); + return true; +} + +//------------------------------------------------------------------------ +// VstSilenceProcessingTest +//------------------------------------------------------------------------ +VstSilenceProcessingTest::VstSilenceProcessingTest (IPlugProvider* plugProvider, ProcessSampleSize sampl) +: VstProcessTest (plugProvider, sampl) +{ +} + +//------------------------------------------------------------------------ +bool VstSilenceProcessingTest::isBufferSilent (void* buffer, int32 numSamples, ProcessSampleSize sampl) +{ + if (sampl == kSample32) + { + const float kSilenceThreshold = 0.000132184039f; + + float* floatBuffer = (float*)buffer; + while (numSamples--) + { + if (fabsf (*floatBuffer) > kSilenceThreshold) + return false; + floatBuffer++; + } + } + else if (sampl == kSample64) + { + const double kSilenceThreshold = 0.000132184039; + + double* floatBuffer = (double*)buffer; + while (numSamples--) + { + if (fabs (*floatBuffer) > kSilenceThreshold) + return false; + floatBuffer++; + } + } + return true; +} + +//------------------------------------------------------------------------ +bool PLUGIN_API VstSilenceProcessingTest::run (ITestResult* testResult) +{ + if (!vstPlug || !testResult || !audioEffect) + return false; + if (!canProcessSampleSize (testResult)) + return true; + + printTestHeader (this, testResult); + + if (processData.inputs != 0) + { + audioEffect->setProcessing (true); + + for (int32 busIndex = 0; busIndex < processData.numInputs; busIndex++) + { + processData.inputs[busIndex].silenceFlags = 0; + for (int32 channelIndex = 0; channelIndex < processData.inputs[busIndex].numChannels; channelIndex++) + { + processData.inputs[busIndex].silenceFlags |= (uint64)1 << (uint64)channelIndex; + if (processData.symbolicSampleSize == kSample32) + memset (processData.inputs[busIndex].channelBuffers32[channelIndex], 0, sizeof (float) * processData.numSamples); + else if (processData.symbolicSampleSize == kSample64) + memset (processData.inputs[busIndex].channelBuffers32[channelIndex], 0, sizeof (double) * processData.numSamples); + } + } + + for (int32 busIndex = 0; busIndex < processData.numOutputs; busIndex++) + { + if (processData.numInputs > busIndex) + processData.outputs[busIndex].silenceFlags = processData.inputs[busIndex].silenceFlags; + else + { + processData.outputs[busIndex].silenceFlags = 0; + for (int32 channelIndex = 0; channelIndex < processData.inputs[busIndex].numChannels; channelIndex++) + processData.outputs[busIndex].silenceFlags |= (uint64)1 << (uint64)channelIndex; + } + } + + tresult result = audioEffect->process (processData); + if (result != kResultOk) + { + addErrorMessage (testResult, printf ("%s", "The component failed to process!")); + + audioEffect->setProcessing (false); + return false; + } + + for (int32 busIndex = 0; busIndex < processData.numOutputs; busIndex++) + { + for (int32 channelIndex = 0; channelIndex < processData.outputs[busIndex].numChannels; channelIndex++) + { + bool channelShouldBeSilent = (processData.outputs[busIndex].silenceFlags & (uint64)1 << (uint64)channelIndex) != 0; + bool channelIsSilent = isBufferSilent (processData.outputs[busIndex].channelBuffers32[channelIndex], processData.numSamples, processData.symbolicSampleSize); + if (channelShouldBeSilent != channelIsSilent) + { + constexpr auto silentText = STR ("The component reported a wrong silent flag for its output buffer! : output is silent but silenceFlags not set !"); + constexpr auto nonSilentText = STR ("The component reported a wrong silent flag for its output buffer! : silenceFlags is set to silence but output is not silent"); + addMessage (testResult, channelIsSilent ? silentText : nonSilentText); + break; + } + } + } + + } + else if (processData.numInputs > 0) + { + addErrorMessage (testResult, STR ("ProcessData::inputs are 0 but ProcessData::numInputs are nonzero.")); + return false; + } + + audioEffect->setProcessing (false); + return true; +} + +//------------------------------------------------------------------------ +// VstVariableBlockSizeTest +//------------------------------------------------------------------------ +VstVariableBlockSizeTest::VstVariableBlockSizeTest (IPlugProvider* plugProvider, + ProcessSampleSize sampl) +: VstProcessTest (plugProvider, sampl) +{ +} + +//------------------------------------------------------------------------ +bool PLUGIN_API VstVariableBlockSizeTest::run (ITestResult* testResult) +{ + if (!vstPlug || !testResult || !audioEffect) + return false; + if (!canProcessSampleSize (testResult)) + return true; + + printTestHeader (this, testResult); + + audioEffect->setProcessing (true); + + for (int32 i = 0; i <= NUM_ITERATIONS; ++i) + { + int32 sampleFrames = rand () % processSetup.maxSamplesPerBlock; + processData.numSamples = sampleFrames; + if (i == 0) + processData.numSamples = 0; + #if TOUGHTESTS + else if (i == 1) + processData.numSamples = -50000; + else if (i == 2) + processData.numSamples = processSetup.maxSamplesPerBlock * 2; + #endif + tresult result = audioEffect->process (processData); + if ((result != kResultOk) + #if TOUGHTESTS + && (i > 1) + #else + && (i > 0) + #endif + ) + { + addErrorMessage (testResult, printf ("The component failed to process an audioblock of size %i", sampleFrames)); + audioEffect->setProcessing (false); + return false; + } + } + + audioEffect->setProcessing (false); + return true; +} + + +//------------------------------------------------------------------------ +// VstProcessFormatTest +//------------------------------------------------------------------------ +VstProcessFormatTest::VstProcessFormatTest (IPlugProvider* plugProvider, ProcessSampleSize sampl) +: VstProcessTest (plugProvider, sampl) +{} + +//------------------------------------------------------------------------ +bool PLUGIN_API VstProcessFormatTest::run (ITestResult* testResult) +{ + if (!vstPlug || !testResult || !audioEffect) + return false; + if (!canProcessSampleSize (testResult)) + return true; + + printTestHeader (this, testResult); + + int32 numFails = 0; + const int32 numRates = 11; + SampleRate sampleRateFormats [numRates] = {22050., 32000., 44100., 48000., 88200., 96000., 192000., + 1234.5678, 12345.678, 123456.78, 1234567.8}; + + tresult result = vstPlug->setActive (false); + if (result != kResultOk) + { + addErrorMessage (testResult, STR ("IComponent::setActive (false) failed.")); + return false; + } + + addMessage (testResult, STR ("***Tested Sample Rates***")); + + for (int32 i = 0; i < numRates; ++i) + { + processSetup.sampleRate = sampleRateFormats [i]; + result = audioEffect->setupProcessing (processSetup); + if (result == kResultOk) + { + result = vstPlug->setActive (true); + if (result != kResultOk) + { + addErrorMessage (testResult, STR ("IComponent::setActive (true) failed.")); + return false; + } + + audioEffect->setProcessing (true); + result = audioEffect->process (processData); + audioEffect->setProcessing (false); + + if (result == kResultOk) + { + addMessage (testResult, printf (" %10.10G Hz - processed successfully!", sampleRateFormats [i])); + } + else + { + numFails++; + addErrorMessage (testResult, printf (" %10.10G Hz - failed to process!", sampleRateFormats [i])); + } + + result = vstPlug->setActive (false); + if (result != kResultOk) + { + addErrorMessage (testResult, STR ("IComponent::setActive (false) failed.")); + return false; + } + } + else if (sampleRateFormats[i] > 0.) + { + addErrorMessage (testResult, printf ("IAudioProcessor::setupProcessing (..) failed for samplerate %.3f Hz! ", sampleRateFormats [i])); + //return false; + } + } + + result = vstPlug->setActive (true); + if (result != kResultOk) + return false; + + return true; +} + +//------------------------------------------------------------------------ +// VstBusActivationTest +//------------------------------------------------------------------------ +VstBusActivationTest::VstBusActivationTest (IPlugProvider* plugProvider) +: VstTestBase (plugProvider) +{} + +//------------------------------------------------------------------------ +bool PLUGIN_API VstBusActivationTest::run (ITestResult* testResult) +{ + if (!vstPlug || !testResult) + return false; + printTestHeader (this, testResult); + + int32 numTotalBusses = 0; + int32 numFailedActivations = 0; + + for (MediaType type = kAudio; type < kNumMediaTypes; type++) + { + int32 numInputs = vstPlug->getBusCount (type, kInput); + int32 numOutputs = vstPlug->getBusCount (type, kOutput); + + numTotalBusses += (numInputs + numOutputs); + + for (int32 i = 0; i < numInputs + numOutputs; ++i) + { + BusDirection busDirection = i < numInputs ? kInput : kOutput; + int32 busIndex = busDirection == kInput ? i : i - numInputs; + + BusInfo busInfo = {0}; + if (vstPlug->getBusInfo (type, busDirection, busIndex, busInfo) != kResultTrue) + { + addErrorMessage (testResult, STR ("IComponent::getBusInfo (..) failed.")); + return false; + } + + addMessage (testResult, printf (" Bus Activation: %s %s Bus (%d) (%s)", + busDirection == kInput ? "Input" : "Output", + type == kAudio ? "Audio" : "Event", busIndex, + busInfo.busType == kMain ? "kMain" : "kAux")); + + if ((busInfo.flags & BusInfo::kDefaultActive) == false) + { + if (vstPlug->activateBus (type, busDirection, busIndex, true) != kResultOk) + numFailedActivations++; + if (vstPlug->activateBus (type, busDirection, busIndex, false) != kResultOk) + numFailedActivations++; + } + else if ((busInfo.flags & BusInfo::kDefaultActive) == true) + { + if (vstPlug->activateBus (type, busDirection, busIndex, false) != kResultOk) + numFailedActivations++; + if (vstPlug->activateBus (type, busDirection, busIndex, true) != kResultOk) + numFailedActivations++; + } + } + } + + if (numFailedActivations > 0) + addErrorMessage (testResult, STR ("Bus activation failed.")); + + return (numFailedActivations == 0); +} + + +//------------------------------------------------------------------------ +// VstCheckAudioBusArrangementTest +//------------------------------------------------------------------------ +VstCheckAudioBusArrangementTest::VstCheckAudioBusArrangementTest (IPlugProvider* plugProvider) +: VstTestBase (plugProvider) +{ +} + +//------------------------------------------------------------------------ +bool VstCheckAudioBusArrangementTest::run (ITestResult* testResult) +{ + if (!vstPlug || !testResult) + return false; + + printTestHeader (this, testResult); + + int32 numInputs = vstPlug->getBusCount (kAudio, kInput); + int32 numOutputs = vstPlug->getBusCount (kAudio, kOutput); + int32 arrangementMismatchs = 0; + + FUnknownPtr audioEffect (vstPlug); + if (audioEffect) + { + for (int32 i = 0; i < numInputs + numOutputs; ++i) + { + BusDirection dir = i < numInputs ? kInput : kOutput; + int32 busIndex = dir == kInput ? i : i - numInputs; + + addMessage (testResult, printf (" Check %s Audio Bus Arrangement (%d)", dir == kInput ? "Input" : "Output", busIndex)); + + BusInfo busInfo = {0}; + if (vstPlug->getBusInfo (kAudio, dir, busIndex, busInfo) == kResultTrue) + { + SpeakerArrangement arrangement; + if (audioEffect->getBusArrangement (dir, busIndex, arrangement) == kResultTrue) + { + if (busInfo.channelCount != SpeakerArr::getChannelCount (arrangement)) + { + arrangementMismatchs++; + addErrorMessage (testResult, STR ("channelCount is inconsistent!")); + } + } + else + { + addErrorMessage (testResult, STR ("IAudioProcessor::getBusArrangement (..) failed!")); + return false; + } + } + else + { + addErrorMessage (testResult, STR ("IComponent::getBusInfo (..) failed!")); + return false; + } + } + } + return (arrangementMismatchs == 0); +} + +//------------------------------------------------------------------------ +// VstProcessTailTest +//------------------------------------------------------------------------ +VstProcessTailTest::VstProcessTailTest (IPlugProvider* plugProvider, ProcessSampleSize sampl) +: VstProcessTest (plugProvider, sampl) +, mTailSamples (0) +, mInTail (0) +, dataPtrFloat (nullptr) +, dataPtrDouble (nullptr) +, mInSilenceInput (false) +, mDontTest (false) +{ + FUNKNOWN_CTOR +} + +//------------------------------------------------------------------------ +VstProcessTailTest::~VstProcessTailTest () +{ + if (dataPtrFloat) + { + delete []dataPtrFloat; + dataPtrFloat = nullptr; + } + if (dataPtrDouble) + { + delete []dataPtrDouble; + dataPtrDouble = nullptr; + } +} + +//------------------------------------------------------------------------ +bool PLUGIN_API VstProcessTailTest::setup () +{ + bool result = VstProcessTest::setup (); + if (result) + { + mTailSamples = audioEffect->getTailSamples (); + + std::string subCat (plugProvider->getSubCategories ()); + if (subCat.find ("Generator") != std::string::npos || subCat.find ("Instrument") != std::string::npos) + { + mDontTest = true; + } + } + + return result; +} + +//------------------------------------------------------------------------ +bool VstProcessTailTest::preProcess (ITestResult* testResult) +{ + if (!mInSilenceInput) + { + if (processSetup.symbolicSampleSize == kSample32) + { + if (!dataPtrFloat) + dataPtrFloat = new float[processData.numSamples]; + float* ptr = dataPtrFloat; + for (int32 i = 0; i < processData.numSamples ; ++i) + ptr[i] = (float)(2 * rand () / 32767.0 - 1); + } + else + { + if (!dataPtrDouble) + dataPtrDouble = new double[processData.numSamples]; + double* ptr = (double*)dataPtrDouble; + for (int32 i = 0; i < processData.numSamples ; ++i) + ptr[i] = (double)(2 * rand () / 32767.0 - 1); + } + for (int32 i = 0; i < processData.numOutputs; ++i) + { + for (int32 c = 0; c < processData.outputs->numChannels; ++c) + { + if (processSetup.symbolicSampleSize == kSample32) + memset (processData.outputs->channelBuffers32[c], 0, processData.numSamples * sizeof (float)); + else + memset (processData.outputs->channelBuffers64[c], 0, processData.numSamples * sizeof (double)); + } + } + for (int32 i = 0; i < processData.numInputs ; ++i) + { + for (int32 c = 0; c < processData.inputs->numChannels; ++c) + { + if (processSetup.symbolicSampleSize == kSample32) + memcpy (processData.inputs->channelBuffers32[c], dataPtrFloat, processData.numSamples * sizeof (float)); + else + memcpy (processData.inputs->channelBuffers64[c], dataPtrDouble, processData.numSamples * sizeof (double)); + } + } + } + else + { + // process with silent buffers + for (int32 i = 0; i < processData.numOutputs; ++i) + { + for (int32 c = 0; c < processData.outputs->numChannels; ++c) + { + if (processSetup.symbolicSampleSize == kSample32) + memset (processData.outputs->channelBuffers32[c], 0, processData.numSamples * sizeof (float)); + else + memset (processData.outputs->channelBuffers64[c], 0, processData.numSamples * sizeof (double)); + } + } + for (int32 i = 0; i < processData.numInputs; ++i) + { + for (int32 c = 0; c < processData.inputs->numChannels; ++c) + { + if (processSetup.symbolicSampleSize == kSample32) + memset (processData.inputs->channelBuffers32[c], 0, processData.numSamples * sizeof (float)); + else + memset (processData.inputs->channelBuffers64[c], 0, processData.numSamples * sizeof (double)); + } + } + } + + return true; +} + +//------------------------------------------------------------------------ +bool VstProcessTailTest::postProcess (ITestResult* testResult) +{ + if (mInSilenceInput) + { + // should be silence + if (mTailSamples < mInTail + processData.numSamples) + { + int32 start = mTailSamples > mInTail ? mTailSamples - mInTail : 0; + int32 end = processData.numSamples; + + for (int32 i = 0; i < processData.numOutputs; ++i) + { + for (int32 c = 0; c < processData.outputs->numChannels; ++c) + { + if (processSetup.symbolicSampleSize == kSample32) + { + for (int32 s = start; s < end; ++s) + { + if (fabsf (processData.outputs->channelBuffers32[c][s]) >= 1e-7) + { + addErrorMessage (testResult, printf ("IAudioProcessor::process (..) generates non silent output for silent input for tail above %d samples.", mTailSamples)); + return false; + } + } + } + else + { + for (int32 s = start; s < end; ++s) + { + if (fabs (processData.outputs->channelBuffers64[c][s]) >= 1e-7) + { + addErrorMessage (testResult, printf ("IAudioProcessor::process (..) generates non silent output for silent input for tail above %d samples.", mTailSamples)); + return false; + } + } + } + + } + } + } + mInTail += processData.numSamples; + } + return true; +} + +//------------------------------------------------------------------------ +bool PLUGIN_API VstProcessTailTest::run (ITestResult* testResult) +{ + if (!testResult || !audioEffect) + return false; + if (processSetup.symbolicSampleSize != processData.symbolicSampleSize) + return false; + if (!canProcessSampleSize (testResult)) + return true; + + if (mDontTest) + return true; + + addMessage (testResult, printf ("===%s == Tail=%d ======================", getName (), mTailSamples)); + + audioEffect->setProcessing (true); + + // process with signal (noise) and silence + for (int32 i = 0; i < 20 * NUM_AUDIO_BLOCKS_TO_PROCESS; ++i) + { + mInSilenceInput = i > 10; + + if (!preProcess (testResult)) + return false; + tresult result = audioEffect->process (processData); + if (result != kResultOk) + { + addErrorMessage (testResult, STR ("IAudioProcessor::process (..) failed.")); + + audioEffect->setProcessing (false); + return false; + } + if (!postProcess (testResult)) + { + audioEffect->setProcessing (false); + return false; + } + } + + audioEffect->setProcessing (false); + return true; +} + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/testsuite/vsttestsuite.h b/source/includes/vst3sdk/public.sdk/source/vst/testsuite/vsttestsuite.h new file mode 100644 index 000000000..99a00dc6a --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/testsuite/vsttestsuite.h @@ -0,0 +1,720 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Validator +// Filename : public.sdk/source/vst/vsttestsuite.h +// Created by : Steinberg, 04/2005 +// Description : VST Test Suite +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "pluginterfaces/test/itest.h" +#include "public.sdk/source/vst/hosting/processdata.h" + +// VST 3 interfaces +#include "pluginterfaces/base/ipluginbase.h" +#include "pluginterfaces/vst/ivstcomponent.h" +#include "pluginterfaces/vst/ivstaudioprocessor.h" +#include "pluginterfaces/vst/ivsteditcontroller.h" +#include "pluginterfaces/vst/ivstparameterchanges.h" + +namespace Steinberg { +namespace Vst { + +typedef int32 ProcessSampleSize; + +//------------------------------------------------------------------------ +#define DECLARE_VSTTEST(name) \ +virtual const char* getName () const SMTG_OVERRIDE { return name; } + +/** Set from outside the plug-in context (simulating a host context) */ +extern void setStandardPluginContext (FUnknown* context); + +//------------------------------------------------------------------------ +/** Test Helper. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class IPlugProvider : public FUnknown +{ +public: +//------------------------------------------------------------------------ + virtual IComponent* getComponent () = 0; + virtual IEditController* getController () = 0; + virtual tresult releasePlugIn (IComponent* component, IEditController* controller) = 0; + virtual const char8* getSubCategories () const = 0; + virtual tresult getPluginUID (FUID& uid) const = 0; + +//------------------------------------------------------------------------ + static const FUID iid; +}; + +DECLARE_CLASS_IID (IPlugProvider, 0xDB014121, 0x09144BAA, 0x87627896, 0xBE41AF5D) + +//------------------------------------------------------------------------ +/** VstAutomationTest helper classes. +\ingroup TestClass */ +class ParamPoint +{ +public: + ParamPoint () : offsetSamples (-1), value (0.), read (false) {} + void set (int32 offsetSamples, double value) + { + this->offsetSamples = offsetSamples; + this->value = value; + } + void get (int32& offsetSamples, double& value) + { + offsetSamples = this->offsetSamples; + value = this->value; + read = true; + } + bool wasRead () const { return read; } + +private: + int32 offsetSamples; + double value; + bool read; +}; + +//------------------------------------------------------------------------ +/** VstAutomationTest helper classes: implementation of IParamValueQueue. +\ingroup TestClass */ +class ParamChanges : public IParamValueQueue +{ +public: + ParamChanges () : id (-1), numPoints (0), numUsedPoints (0), processedFrames (0), points (nullptr) + { + FUNKNOWN_CTOR + } + virtual ~ParamChanges () + { + if (points) + delete[] points; + FUNKNOWN_DTOR + } + + DECLARE_FUNKNOWN_METHODS + + void init (ParamID id, int32 numPoints) + { + this->id = id; + this->numPoints = numPoints; + numUsedPoints = 0; + if (points) + delete[] points; + points = new ParamPoint[numPoints]; + processedFrames = 0; + } + bool setPoint (int32 index, int32 offsetSamples, double value) + { + if (points && (index >= 0) && (index == numUsedPoints) && (index < numPoints)) + { + points[index].set (offsetSamples, value); + numUsedPoints++; + return true; + } + if (!points) + return true; + return false; + } + void resetPoints () + { + numUsedPoints = 0; + processedFrames = 0; + } + int32 getProcessedFrames () const { return processedFrames; } + void setProcessedFrames (int32 amount) { processedFrames = amount; } + bool havePointsBeenRead (bool atAll) + { + for (int32 i = 0; i < getPointCount (); ++i) + { + if (points[i].wasRead ()) + { + if (atAll) + return true; + } + else if (!atAll) + return false; + } + return !atAll; + } + + //---for IParamValueQueue------------------------- + ParamID PLUGIN_API getParameterId () SMTG_OVERRIDE { return id; } + int32 PLUGIN_API getPointCount () SMTG_OVERRIDE { return numUsedPoints; } + tresult PLUGIN_API getPoint (int32 index, int32& offsetSamples, double& value) SMTG_OVERRIDE + { + if (points && (index < numUsedPoints) && (index >= 0)) + { + points[index].get (offsetSamples, value); + return kResultTrue; + } + return kResultFalse; + } + tresult PLUGIN_API addPoint (int32 offsetSamples, double value, int32& index) SMTG_OVERRIDE + { + return kResultFalse; + } +//--------------------------------------------------------- + +private: + ParamID id; + int32 numPoints; + int32 numUsedPoints; + int32 processedFrames; + ParamPoint* points; +}; + +//------------------------------------------------------------------------ +/** Test Helper. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstTestBase: public ITest +{ +public: +//------------------------------------------------------------------------ + VstTestBase (IPlugProvider* plugProvider); + virtual ~VstTestBase (); + + virtual const char* getName () const = 0; + DECLARE_FUNKNOWN_METHODS + + bool PLUGIN_API setup () SMTG_OVERRIDE; + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE {return false;} // implement me + bool PLUGIN_API teardown () SMTG_OVERRIDE; +//------------------------------------------------------------------------ +protected: + IPlugProvider* plugProvider; + IComponent* vstPlug; + IEditController* controller; + +private: + VstTestBase (); +}; + +//------------------------------------------------------------------------ +/** Test Helper. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstTestEnh : public VstTestBase +{ +public: +//------------------------------------------------------------------------ + VstTestEnh (IPlugProvider* plugProvider, ProcessSampleSize sampl); + virtual ~VstTestEnh (); + + enum AudioDefaults + { + kBlockSize = 64, + kMaxSamplesPerBlock = 8192, + kSampleRate = 44100, + }; + + bool PLUGIN_API setup () SMTG_OVERRIDE; + bool PLUGIN_API teardown () SMTG_OVERRIDE; +//------------------------------------------------------------------------ +protected: + // interfaces + IAudioProcessor* audioEffect; + + ProcessSetup processSetup; +}; + +//------------------------------------------------------------------------ +/** Test Suspend/Resume. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstSuspendResumeTest: public VstTestEnh +{ +public: +//------------------------------------------------------------------------ + VstSuspendResumeTest (IPlugProvider* plugProvider, ProcessSampleSize sampl); + + DECLARE_VSTTEST ("Suspend/Resume") + + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; +//------------------------------------------------------------------------ +}; + + +//------------------------------------------------------------------------ +/** Test Terminate/Initialize. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstTerminateInitializeTest: public VstTestBase +{ +public: +//------------------------------------------------------------------------ + VstTerminateInitializeTest (IPlugProvider* plugProvider); + + DECLARE_VSTTEST ("Terminate/Initialize") + + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; +//------------------------------------------------------------------------ +}; + +//------------------------------------------------------------------------ +/** Test Scan Buses. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstScanBussesTest: public VstTestBase +{ +public: +//------------------------------------------------------------------------ + VstScanBussesTest (IPlugProvider* plugProvider); + + DECLARE_VSTTEST ("Scan Buses") + + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; +//------------------------------------------------------------------------ +}; + +//------------------------------------------------------------------------ +/** Test Scan Parameters. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstScanParametersTest: public VstTestBase +{ +public: +//------------------------------------------------------------------------ + VstScanParametersTest (IPlugProvider* plugProvider); + + DECLARE_VSTTEST ("Scan Parameters") + + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; +//------------------------------------------------------------------------ +}; + +//------------------------------------------------------------------------ +/** Test MIDI Mapping. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstMidiMappingTest: public VstTestBase +{ +public: +//------------------------------------------------------------------------ + VstMidiMappingTest (IPlugProvider* plugProvider); + + DECLARE_VSTTEST ("MIDI Mapping") + + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; +//------------------------------------------------------------------------ +}; + + + +//------------------------------------------------------------------------ +/** Test Note Expression. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstNoteExpressionTest: public VstTestBase +{ +public: + //------------------------------------------------------------------------ + VstNoteExpressionTest (IPlugProvider* plugProvider); + + DECLARE_VSTTEST ("Note Expression") + + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; + //------------------------------------------------------------------------ +}; + +//------------------------------------------------------------------------ +/** Test Keyswitch. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstKeyswitchTest: public VstTestBase +{ +public: + //------------------------------------------------------------------------ + VstKeyswitchTest (IPlugProvider* plugProvider); + + DECLARE_VSTTEST ("Keyswitch") + + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; + //------------------------------------------------------------------------ +}; + +//------------------------------------------------------------------------ +/** Test Scan Editor Classes. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstEditorClassesTest: public VstTestBase +{ +public: +//------------------------------------------------------------------------ + VstEditorClassesTest (IPlugProvider* plugProvider); + + DECLARE_VSTTEST ("Scan Editor Classes") + + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; +//------------------------------------------------------------------------ +}; + +//------------------------------------------------------------------------ +/** Test Scan Units. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstUnitInfoTest: public VstTestBase +{ +public: +//------------------------------------------------------------------------ + VstUnitInfoTest (IPlugProvider* plugProvider); + + DECLARE_VSTTEST ("Scan Units") + + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; +//------------------------------------------------------------------------ +}; + +//------------------------------------------------------------------------ +/** Test Scan Programs. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstProgramInfoTest: public VstTestBase +{ +public: +//------------------------------------------------------------------------ + VstProgramInfoTest (IPlugProvider* plugProvider); + + DECLARE_VSTTEST ("Scan Programs") + + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; +//------------------------------------------------------------------------ +}; + +//------------------------------------------------------------------------ +/** Test Check Unit Structure. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstUnitStructureTest: public VstTestBase +{ +public: +//------------------------------------------------------------------------ + VstUnitStructureTest (IPlugProvider* plugProvider); + + DECLARE_VSTTEST ("Check Unit Structure") + + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; +//------------------------------------------------------------------------ +}; + +//------------------------------------------------------------------------ +/** Test Process Test. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstProcessTest : public VstTestEnh +{ +public: +//------------------------------------------------------------------------ + VstProcessTest (IPlugProvider* plugProvider, ProcessSampleSize sampl); + + DECLARE_VSTTEST ("Process Test") + + // ITest + bool PLUGIN_API setup () SMTG_OVERRIDE; + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; + bool PLUGIN_API teardown () SMTG_OVERRIDE; + +//------------------------------------------------------------------------ +protected: + virtual bool prepareProcessing (); ///< setup ProcessData and allocate buffers + virtual bool unprepareProcessing (); ///< free dynamic memory of ProcessData + virtual bool preProcess (ITestResult* testResult); ///< is called just before the process call + virtual bool postProcess (ITestResult* testResult); ///< is called right after the process call + + bool setupBuffers (int32 numBusses, AudioBusBuffers* audioBuffers, BusDirection dir); + bool setupBuffers (AudioBusBuffers& audioBuffers); + bool freeBuffers (int32 numBuses, AudioBusBuffers* buses); + bool canProcessSampleSize (ITestResult* testResult); ///< audioEffect has to be available + + HostProcessData processData; +}; + +//------------------------------------------------------------------------ +/** Test Speaker Arrangement. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstSpeakerArrangementTest : public VstProcessTest +{ +public: +//------------------------------------------------------------------------ + VstSpeakerArrangementTest (IPlugProvider* plugProvider, ProcessSampleSize sampl, SpeakerArrangement inSpArr, SpeakerArrangement outSpArr); + + const char* getName () const SMTG_OVERRIDE; + static const char* getSpeakerArrangementName (SpeakerArrangement spArr); + + // ITest + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; + +//------------------------------------------------------------------------ +protected: + bool prepareProcessing () SMTG_OVERRIDE; + bool verifySA (int32 numBusses, AudioBusBuffers* buses, SpeakerArrangement spArr, ITestResult* testResult); +private: + SpeakerArrangement inSpArr; + SpeakerArrangement outSpArr; +}; + + +class ParamChanges; +//------------------------------------------------------------------------ +/** Test Automation. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstAutomationTest : public VstProcessTest, public IParameterChanges +{ +public: +//------------------------------------------------------------------------ + VstAutomationTest (IPlugProvider* plugProvider, ProcessSampleSize sampl, int32 everyNSamples, int32 numParams, bool sampleAccuracy); + virtual ~VstAutomationTest (); + + DECLARE_FUNKNOWN_METHODS + const char* getName () const SMTG_OVERRIDE; + // ITest + bool PLUGIN_API setup () SMTG_OVERRIDE; + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; + bool PLUGIN_API teardown () SMTG_OVERRIDE; + + // IParameterChanges + int32 PLUGIN_API getParameterCount () SMTG_OVERRIDE; + IParamValueQueue* PLUGIN_API getParameterData (int32 index) SMTG_OVERRIDE; + IParamValueQueue* PLUGIN_API addParameterData (const ParamID& id, int32& index) SMTG_OVERRIDE; + +//------------------------------------------------------------------------ +protected: + bool preProcess (ITestResult* testResult) SMTG_OVERRIDE; + bool postProcess (ITestResult* testResult) SMTG_OVERRIDE; + ParamID bypassId; + + ParamChanges* paramChanges; + int32 countParamChanges; + int32 everyNSamples; + int32 numParams; + bool sampleAccuracy; + bool onceExecuted; +}; + +//------------------------------------------------------------------------ +/** Test Valid State Transition. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstValidStateTransitionTest : public VstTestBase +{ +public: + VstValidStateTransitionTest (IPlugProvider* plugProvider); + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; + + DECLARE_VSTTEST ("Valid State Transition") +}; + +//------------------------------------------------------------------------ +/** Test Invalid State Transition. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstInvalidStateTransitionTest : public VstTestBase +{ +public: + VstInvalidStateTransitionTest (IPlugProvider* plugProvider); + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; + + DECLARE_VSTTEST ("Invalid State Transition") +}; + +//------------------------------------------------------------------------ +/** Test Repeat Identical State Transition. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstRepeatIdenticalStateTransitionTest : public VstTestBase +{ +public: + VstRepeatIdenticalStateTransitionTest (IPlugProvider* plugProvider); + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; + + DECLARE_VSTTEST ("Repeat Identical State Transition") +}; + +//------------------------------------------------------------------------ +/** Test Bus Consistency. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstBusConsistencyTest : public VstTestBase +{ +public: + VstBusConsistencyTest (IPlugProvider* plugProvider); + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; + + DECLARE_VSTTEST ("Bus Consistency") +}; + +//------------------------------------------------------------------------ +/** Test Bus Invalid Index. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstBusInvalidIndexTest : public VstTestBase +{ +public: + VstBusInvalidIndexTest (IPlugProvider* plugProvider); + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; + + DECLARE_VSTTEST ("Bus Invalid Index") +}; + +//------------------------------------------------------------------------ +/** Test Silence Flags. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstSilenceFlagsTest : public VstProcessTest +{ +public: + VstSilenceFlagsTest (IPlugProvider* plugProvider, ProcessSampleSize sampl); + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; + + DECLARE_VSTTEST ("Silence Flags") +}; + +//------------------------------------------------------------------------ +/** Test Silence Processing. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstSilenceProcessingTest : public VstProcessTest +{ +public: + VstSilenceProcessingTest (IPlugProvider* plugProvider, ProcessSampleSize sampl); + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; + + DECLARE_VSTTEST ("Silence Processing") +protected: + bool isBufferSilent (void* buffer, int32 numSamples, ProcessSampleSize sampl); +}; + +//------------------------------------------------------------------------ +/** Test Parameters Flush (no Buffer). +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstFlushParamTest : public VstAutomationTest +{ +public: + VstFlushParamTest (IPlugProvider* plugProvider, ProcessSampleSize sampl); + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; + + DECLARE_VSTTEST ("Parameters Flush (no Buffer)") +}; + +//------------------------------------------------------------------------ +/** Test Bus Activation. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstBusActivationTest : public VstTestBase +{ +public: + VstBusActivationTest (IPlugProvider* plugProvider); + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; + + DECLARE_VSTTEST ("Bus Activation") +}; + +//------------------------------------------------------------------------ +/** Test Variable Block Size. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstVariableBlockSizeTest : public VstProcessTest +{ +public: + VstVariableBlockSizeTest (IPlugProvider* plugProvider, ProcessSampleSize sampl); + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; + + DECLARE_VSTTEST ("Variable Block Size") +}; + +//------------------------------------------------------------------------ +/** Test Process Format. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstProcessFormatTest : public VstProcessTest +{ +public: + VstProcessFormatTest (IPlugProvider* plugProvider, ProcessSampleSize sampl); + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; + + DECLARE_VSTTEST ("Process Format") +}; + +//------------------------------------------------------------------------ +/** Test Check Audio Bus Arrangement. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstCheckAudioBusArrangementTest : public VstTestBase +{ +public: + VstCheckAudioBusArrangementTest (IPlugProvider* plugProvider); + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; + + DECLARE_VSTTEST ("Check Audio Bus Arrangement") +}; + + +//------------------------------------------------------------------------ +/** Test ProcesTail. +\ingroup TestClass */ +//------------------------------------------------------------------------ +class VstProcessTailTest : public VstProcessTest +{ +public: + //------------------------------------------------------------------------ + VstProcessTailTest (IPlugProvider* plugProvider, ProcessSampleSize sampl); + virtual ~VstProcessTailTest (); + + DECLARE_VSTTEST ("Check Tail processing") + + // ITest + bool PLUGIN_API setup () SMTG_OVERRIDE; + bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; + bool preProcess (ITestResult* testResult) SMTG_OVERRIDE; + bool postProcess (ITestResult* testResult) SMTG_OVERRIDE; + + //------------------------------------------------------------------------ +protected: + +private: + uint32 mTailSamples; + uint32 mInTail; + + float* dataPtrFloat; + double* dataPtrDouble; + bool mInSilenceInput; + bool mDontTest; +}; + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg + diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vst2wrapper/vst2wrapper.cpp b/source/includes/vst3sdk/public.sdk/source/vst/vst2wrapper/vst2wrapper.cpp new file mode 100644 index 000000000..76e975e59 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vst2wrapper/vst2wrapper.cpp @@ -0,0 +1,3105 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vst2wrapper/vst2wrapper.cpp +// Created by : Steinberg, 01/2009 +// Description : VST 3 -> VST 2 Wrapper +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +/// \cond ignore + +#include "public.sdk/source/vst/vst2wrapper/vst2wrapper.h" + +#include "public.sdk/source/vst/hosting/hostclasses.h" +#include "public.sdk/source/vst2.x/aeffeditor.h" + +#include "pluginterfaces/gui/iplugview.h" +#include "pluginterfaces/vst/ivstmidicontrollers.h" +#include "pluginterfaces/vst/ivstmessage.h" +#include "pluginterfaces/vst/vstpresetkeys.h" +#include "pluginterfaces/base/futils.h" +#include "pluginterfaces/base/keycodes.h" + +#include "base/source/fstreamer.h" + +#include +#include +#include + +extern bool DeinitModule (); //! Called in Vst2Wrapper destructor + +//------------------------------------------------------------------------ +// some Defines +//------------------------------------------------------------------------ +// should be kVstMaxParamStrLen if we want to respect the VST 2 specification!!! +#define kVstExtMaxParamStrLen 32 + +//------------------------------------------------------------------------ +namespace Steinberg { +namespace Vst { + +//! The parameter's name contains the unit path (e.g. "Modulators.LFO 1.frequency") +bool vst2WrapperFullParameterPath = true; + +//------------------------------------------------------------------------ +// some Globals +//------------------------------------------------------------------------ +// In order to speed up hasEditor function gPluginHasEditor can be set in EditController::initialize +enum +{ + kDontKnow = -1, + kNoEditor = 0, + kEditor, +}; + +// default: kDontKnow which uses createView to find out +typedef int32 EditorAvailability; +EditorAvailability gPluginHasEditor = kDontKnow; + +// Set to 'true' in EditController::initialize +// default: VST 3 kIsProgramChange parameter will not be exported in VST 2 +bool gExportProgramChangeParameters = false; + +//------------------------------------------------------------------------ +// Vst2EditorWrapper Declaration +//------------------------------------------------------------------------ +class Vst2EditorWrapper : public AEffEditor, public IPlugFrame +{ +public: +//------------------------------------------------------------------------ + Vst2EditorWrapper (AudioEffect* effect, IEditController* controller); + ~Vst2EditorWrapper (); + + static bool hasEditor (IEditController* controller); + + virtual bool getRect (ERect** rect); + virtual bool open (void* ptr); + virtual void close (); + virtual bool setKnobMode (VstInt32 val); + + ///< Receives key down event. Return true only if key was really used! + virtual bool onKeyDown (VstKeyCode& keyCode); + ///< Receives key up event. Return true only if key was really used! + virtual bool onKeyUp (VstKeyCode& keyCode); + ///< Handles mouse wheel event, distance is positive or negative to indicate wheel direction. + virtual bool onWheel (float distance); + + // IPlugFrame + virtual tresult PLUGIN_API resizeView (IPlugView* view, ViewRect* newSize); + virtual tresult PLUGIN_API queryInterface (const char* _iid, void** obj); + virtual uint32 PLUGIN_API addRef () { return 1; } + virtual uint32 PLUGIN_API release () { return 1; } +//------------------------------------------------------------------------ +protected: + void createView (); + + IEditController* mController; + IPlugView* mView; + + ERect mERect; +}; + +//------------------------------------------------------------------------ +// Vst2EditorWrapper Implementation +//------------------------------------------------------------------------ +Vst2EditorWrapper::Vst2EditorWrapper (AudioEffect* effect, IEditController* controller) +: AEffEditor (effect), mController (controller), mView (0) +{ + if (mController) + mController->addRef (); +} + +//------------------------------------------------------------------------ +Vst2EditorWrapper::~Vst2EditorWrapper () +{ + if (mView) + close (); + + if (mController) + mController->release (); +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API Vst2EditorWrapper::queryInterface (const char* _iid, void** obj) +{ + QUERY_INTERFACE (_iid, obj, FUnknown::iid, FUnknown) + QUERY_INTERFACE (_iid, obj, IPlugFrame::iid, IPlugFrame) + + *obj = 0; + return kNoInterface; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API Vst2EditorWrapper::resizeView (IPlugView* view, ViewRect* newSize) +{ + tresult result = kResultFalse; + if (view && newSize) + { + if (effect) + { + AudioEffectX* effectx = dynamic_cast (effect); + if (effectx && effectx->sizeWindow (newSize->getWidth (), newSize->getHeight ())) + { +#if MAC + ViewRect nSize (0, 0, newSize->right - newSize->left, newSize->bottom - newSize->top); + result = view->onSize (&nSize); +#else + result = view->onSize (newSize); +#endif + } + } + } + + return result; +} + +//------------------------------------------------------------------------ +bool Vst2EditorWrapper::hasEditor (IEditController* controller) +{ + /* Some Plug-ins might have large GUIs. In order to speed up hasEditor function while + * initializing the Plug-in gPluginHasEditor can be set in EditController::initialize + * beforehand. */ + bool result = false; + if (gPluginHasEditor == kEditor) + { + result = true; + } + else if (gPluginHasEditor == kNoEditor) + { + result = false; + } + else + { + IPlugView* view = controller ? controller->createView (ViewType::kEditor) : 0; + FReleaser viewRel (view); + result = (view != 0); + } + + return result; +} + +//------------------------------------------------------------------------ +void Vst2EditorWrapper::createView () +{ + if (mView == 0 && mController != 0) + { + mView = mController->createView (ViewType::kEditor); + mView->setFrame (this); + +#if MAC +#if PLATFORM_64 + if (mView && mView->isPlatformTypeSupported (kPlatformTypeNSView) != kResultTrue) +#else + if (mView && mView->isPlatformTypeSupported (kPlatformTypeHIView) != kResultTrue) +#endif + { + mView->release (); + mView = nullptr; + mController->release (); // do not try again + mController = nullptr; + } +#endif + } +} + +//------------------------------------------------------------------------ +bool Vst2EditorWrapper::getRect (ERect** rect) +{ + createView (); + if (mView) + { + ViewRect size; + if (mView->getSize (&size) == kResultTrue) + { + mERect.left = (VstInt16)size.left; + mERect.top = (VstInt16)size.top; + mERect.right = (VstInt16)size.right; + mERect.bottom = (VstInt16)size.bottom; + + if ((mERect.bottom - mERect.top) > 0 && (mERect.right - mERect.left) > 0) + { + *rect = &mERect; + return true; + } + } + } + + *rect = 0; + return false; +} + +//------------------------------------------------------------------------ +bool Vst2EditorWrapper::open (void* ptr) +{ + AEffEditor::open (ptr); + createView (); + + if (mView) + { +#if WINDOWS + FIDString type = kPlatformTypeHWND; +#elif MAC && PLATFORM_64 + FIDString type = kPlatformTypeNSView; +#elif MAC + FIDString type = kPlatformTypeHIView; +#endif + return mView->attached (ptr, type) == kResultTrue; + } + return false; +} + +//------------------------------------------------------------------------ +void Vst2EditorWrapper::close () +{ + if (mView) + { + mView->setFrame (0); + mView->removed (); + mView->release (); + mView = nullptr; + } + + AEffEditor::close (); +} + +//------------------------------------------------------------------------ +bool Vst2EditorWrapper::setKnobMode (VstInt32 val) +{ + bool result = false; + IEditController2* editController2; + if (mController && + mController->queryInterface (IEditController2::iid, (void**)&editController2) == + kResultTrue) + { + switch (val) + { + // Circular + case 0: + result = editController2->setKnobMode (Vst::kCircularMode) == kResultTrue; + break; + + // Relative Circular + case 1: + result = editController2->setKnobMode (kRelativCircularMode) == kResultTrue; + break; + + // Linear + case 2: result = editController2->setKnobMode (kLinearMode) == kResultTrue; break; + } + editController2->release (); + } + return result; +} + +//------------------------------------------------------------------------ +bool Vst2EditorWrapper::onKeyDown (VstKeyCode& keyCode) +{ + if (mView) + return mView->onKeyDown (VirtualKeyCodeToChar (keyCode.virt), keyCode.virt, + keyCode.modifier) == kResultTrue; + return false; +} + +//------------------------------------------------------------------------ +bool Vst2EditorWrapper::onKeyUp (VstKeyCode& keyCode) +{ + if (mView) + return mView->onKeyUp (VirtualKeyCodeToChar (keyCode.virt), keyCode.virt, + keyCode.modifier) == kResultTrue; + return false; +} + +//------------------------------------------------------------------------ +bool Vst2EditorWrapper::onWheel (float distance) +{ + if (mView) + return mView->onWheel (distance) == kResultTrue; + return false; +} + +//------------------------------------------------------------------------ +// Vst2MidiEventQueue Declaration +//------------------------------------------------------------------------ +class Vst2MidiEventQueue +{ +public: +//------------------------------------------------------------------------ + Vst2MidiEventQueue (int32 maxEventCount); + ~Vst2MidiEventQueue (); + + bool isEmpty () const { return eventList->numEvents == 0; } + bool add (const VstMidiEvent& e); + bool add (const VstMidiSysexEvent& e); + void flush (); + + operator VstEvents* () { return eventList; } +//------------------------------------------------------------------------ +protected: + VstEvents* eventList; + int32 maxEventCount; +}; + +//------------------------------------------------------------------------ +// Vst2MidiEventQueue Implementation +//------------------------------------------------------------------------ +Vst2MidiEventQueue::Vst2MidiEventQueue (int32 maxEventCount) : maxEventCount (maxEventCount) +{ + eventList = (VstEvents*)new char[sizeof (VstEvents) + (maxEventCount - 2) * sizeof (VstEvent*)]; + eventList->numEvents = 0; + eventList->reserved = 0; + + int32 eventSize = sizeof (VstMidiSysexEvent) > sizeof (VstMidiEvent) ? + sizeof (VstMidiSysexEvent) : + sizeof (VstMidiEvent); + + for (int32 i = 0; i < maxEventCount; i++) + { + char* eventBuffer = new char[eventSize]; + memset (eventBuffer, 0, eventSize); + eventList->events[i] = (VstEvent*)eventBuffer; + } +} + +//------------------------------------------------------------------------ +Vst2MidiEventQueue::~Vst2MidiEventQueue () +{ + for (int32 i = 0; i < maxEventCount; i++) + delete[] (char*) eventList->events[i]; + + delete[] (char*) eventList; +} + +//------------------------------------------------------------------------ +bool Vst2MidiEventQueue::add (const VstMidiEvent& e) +{ + if (eventList->numEvents >= maxEventCount) + return false; + + VstMidiEvent* dst = (VstMidiEvent*)eventList->events[eventList->numEvents++]; + memcpy (dst, &e, sizeof (VstMidiEvent)); + dst->type = kVstMidiType; + dst->byteSize = sizeof (VstMidiEvent); + return true; +} + +//------------------------------------------------------------------------ +bool Vst2MidiEventQueue::add (const VstMidiSysexEvent& e) +{ + if (eventList->numEvents >= maxEventCount) + return false; + + VstMidiSysexEvent* dst = (VstMidiSysexEvent*)eventList->events[eventList->numEvents++]; + memcpy (dst, &e, sizeof (VstMidiSysexEvent)); + dst->type = kVstSysExType; + dst->byteSize = sizeof (VstMidiSysexEvent); + return true; +} + +//------------------------------------------------------------------------ +void Vst2MidiEventQueue::flush () +{ + eventList->numEvents = 0; +} + +//------------------------------------------------------------------------ +// MemoryStream with attributes to add information "preset or project" +//------------------------------------------------------------------------ +class VstPresetStream : public MemoryStream, Vst::IStreamAttributes +{ +public: + VstPresetStream () {} + VstPresetStream (void* memory, TSize memorySize) : MemoryStream (memory, memorySize) {} + + //---from Vst::IStreamAttributes----- + virtual tresult PLUGIN_API getFileName (String128 name) SMTG_OVERRIDE { return kNotImplemented; } + virtual IAttributeList* PLUGIN_API getAttributes () SMTG_OVERRIDE { return &attrList; } + + //------------------------------------------------------------------------ + DELEGATE_REFCOUNT (MemoryStream) + virtual tresult PLUGIN_API queryInterface (const TUID iid, void** obj) SMTG_OVERRIDE + { + QUERY_INTERFACE (iid, obj, IStreamAttributes::iid, IStreamAttributes) + return MemoryStream::queryInterface (iid, obj); + } + +protected: + HostAttributeList attrList; +}; + +//------------------------------------------------------------------------ +// Define the numeric max as "No ParamID" +const ParamID kNoParamId = std::numeric_limits::max (); +const int32 kMaxEvents = 2048; + +//------------------------------------------------------------------------ +// Vst2Wrapper +//------------------------------------------------------------------------ +Vst2Wrapper::Vst2Wrapper (IAudioProcessor* processor, IEditController* controller, + audioMasterCallback audioMaster, const TUID vst3ComponentID, + VstInt32 vst2ID, IPluginFactory* factory) +: AudioEffectX (audioMaster, 0, 0) +, mVst2InputArrangement (nullptr) +, mVst2OutputArrangement (nullptr) +, mProcessor (processor) +, mComponent (nullptr) +, mController (controller) +, mUnitInfo (nullptr) +, mMidiMapping (nullptr) +, componentInitialized (false) +, controllerInitialized (false) +, componentsConnected (false) +, processing (false) +, hasEventInputBuses (false) +, hasEventOutputBuses (false) +, mVst3SampleSize (kSample32) +, mVst3processMode (kRealtime) +, mBypassParameterID (kNoParamId) +, mProgramParameterID (kNoParamId) +, mProgramParameterIdx (-1) +, mInputEvents (nullptr) +, mOutputEvents (nullptr) +, mVst2OutputEvents (nullptr) +, mMainAudioInputBuses (0) +, mMainAudioOutputBuses (0) +, mTimer (nullptr) +, mFactory (factory) +{ + memset (mName, 0, sizeof (mName)); + memset (mVendor, 0, sizeof (mVendor)); + memset (mSubCategories, 0, sizeof (mSubCategories)); + memset (&mProcessContext, 0, sizeof (ProcessContext)); + mVersion = 0; + + for (int32 b = 0; b < kMaxMidiMappingBusses; b++) + for (int32 i = 0; i < 16; i++) + mMidiCCMapping[b][i] = 0; + + // VST 2 stuff ----------------------------------------------- + setUniqueID (vst2ID); // identify + mVst3EffectClassID = vst3ComponentID; + canProcessReplacing (true); // supports replacing output + programsAreChunks (true); + + for (int32 i = 0; i < kMaxProgramChangeParameters; i++) + { + mProgramChangeParameterIDs[i] = kNoParamId; + mProgramChangeParameterIdxs[i] = -1; + } + if (factory) + factory->addRef (); +} + +//------------------------------------------------------------------------ +bool Vst2Wrapper::init () +{ + // VST 3 stuff ----------------------------------------------- + if (mProcessor) + mProcessor->queryInterface (IComponent::iid, (void**)&mComponent); + if (mController) + { + mController->queryInterface (IUnitInfo::iid, (void**)&mUnitInfo); + mController->queryInterface (IMidiMapping::iid, (void**)&mMidiMapping); + } + + //---init the processor component + componentInitialized = true; + if (mComponent->initialize ((IHostApplication*)this) != kResultTrue) + return false; + + //---init the controller component + if (mController) + { + // do not initialize 2 times the component if it is singleComponent + if (FUnknownPtr (mComponent).getInterface () != mController) + { + controllerInitialized = true; + if (mController->initialize ((IHostApplication*)this) != kResultTrue) + return false; + } + + //---set this class as Component Handler + mController->setComponentHandler (this); + + //---connect the 2 components + FUnknownPtr cp1 (mProcessor); + FUnknownPtr cp2 (mController); + if (cp1 && cp2) + { + cp1->connect (cp2); + cp2->connect (cp1); + + componentsConnected = true; + } + + //---inform the component "controller" with the component "processor" state + MemoryStream stream; + if (mComponent->getState (&stream) == kResultTrue) + { + stream.seek (0, IBStream::kIBSeekSet, 0); + mController->setComponentState (&stream); + } + } + + // VST 2 stuff ----------------------------------------------- + if (mProcessor) + { + if (mProcessor->canProcessSampleSize (kSample64) == kResultTrue) + { + canDoubleReplacing (); // supports double precision processing + + // we use the 64 as default only if 32 bit not supported + if (mProcessor->canProcessSampleSize (kSample32) != kResultTrue) + mVst3SampleSize = kSample64; + else + mVst3SampleSize = kSample32; + } + + // latency ------------------------------- + setInitialDelay (mProcessor->getLatencySamples ()); + + if (mProcessor->getTailSamples () == kNoTail) + noTail (true); + + setupProcessing (); // initialize vst3 component processing parameters + } + + // parameters + setupParameters (); + + // setup inputs and outputs + setupBuses (); + + if (mController) + { + if (Vst2EditorWrapper::hasEditor (mController)) + setEditor (new Vst2EditorWrapper (this, mController)); + } + + // find out programs of root unit -------------------------- + numPrograms = cEffect.numPrograms = 0; + if (mUnitInfo) + { + int32 programListCount = mUnitInfo->getProgramListCount (); + if (programListCount > 0) + { + ProgramListID rootUnitProgramListId = kNoProgramListId; + for (int32 i = 0; i < mUnitInfo->getUnitCount (); i++) + { + UnitInfo unit = {0}; + if (mUnitInfo->getUnitInfo (i, unit) == kResultTrue) + { + if (unit.id == kRootUnitId) + { + rootUnitProgramListId = unit.programListId; + break; + } + } + } + + if (rootUnitProgramListId != kNoProgramListId) + { + for (int32 i = 0; i < programListCount; i++) + { + ProgramListInfo progList = {0}; + if (mUnitInfo->getProgramListInfo (i, progList) == kResultTrue) + { + if (progList.id == rootUnitProgramListId) + { + numPrograms = cEffect.numPrograms = progList.programCount; + break; + } + } + } + } + } + } + + if (mTimer == 0) + { + mTimer = Timer::create (this, 50); + } + + initMidiCtrlerAssignment (); + + return true; +} + +//------------------------------------------------------------------------ +Vst2Wrapper::~Vst2Wrapper () +{ + if (mTimer) + { + mTimer->release (); + mTimer = nullptr; + } + + mProcessData.unprepare (); + + if (mVst2InputArrangement) + free (mVst2InputArrangement); + if (mVst2OutputArrangement) + free (mVst2OutputArrangement); + + //---Disconnect components + if (componentsConnected) + { + FUnknownPtr cp1 (mProcessor); + FUnknownPtr cp2 (mController); + if (cp1 && cp2) + { + cp1->disconnect (cp2); + cp2->disconnect (cp1); + } + } + + //---Terminate Controller Component + if (mController) + { + mController->setComponentHandler (0); + if (controllerInitialized) + mController->terminate (); + controllerInitialized = false; + } + + //---Terminate Processor Component + if (mComponent && componentInitialized) + { + mComponent->terminate (); + componentInitialized = false; + } + + if (mUnitInfo) + mUnitInfo->release (); + + if (mMidiMapping) + mMidiMapping->release (); + if (mMidiCCMapping[0]) + for (int32 b = 0; b < kMaxMidiMappingBusses; b++) + for (int32 i = 0; i < 16; i++) + delete mMidiCCMapping[b][i]; + + if (mController) + mController->release (); + + //! editor needs to be destroyed BEFORE DeinitModule. Therefore destroy it here already + // instead of AudioEffect destructor + delete editor; + editor = nullptr; + + if (mComponent) + mComponent->release (); + + if (mProcessor) + mProcessor->release (); + + delete mInputEvents; + delete mOutputEvents; + delete mVst2OutputEvents; + + if (mFactory) + mFactory->release (); + + DeinitModule (); +} + +// VST 2 +//------------------------------------------------------------------------ +void Vst2Wrapper::suspend () +{ + stopProcess (); + + if (mComponent) + mComponent->setActive (false); +} + +//------------------------------------------------------------------------ +void Vst2Wrapper::resume () +{ + AudioEffectX::resume (); + mChunk.setSize (0); + + if (mComponent) + mComponent->setActive (true); +} + +//------------------------------------------------------------------------ +VstInt32 Vst2Wrapper::startProcess () +{ + if (mProcessor && processing == false) + { + processing = true; + mProcessor->setProcessing (true); + } + return 0; +} + +//------------------------------------------------------------------------ +VstInt32 Vst2Wrapper::stopProcess () +{ + if (mProcessor && processing) + { + mProcessor->setProcessing (false); + processing = false; + } + return 0; +} + +//------------------------------------------------------------------------ +bool Vst2Wrapper::setupProcessing (int32 processModeOverwrite) +{ + if (!mProcessor) + return false; + + ProcessSetup setup; + if (processModeOverwrite >= 0) + setup.processMode = processModeOverwrite; + else + setup.processMode = mVst3processMode; + setup.maxSamplesPerBlock = blockSize; + setup.sampleRate = sampleRate; + setup.symbolicSampleSize = mVst3SampleSize; + + return mProcessor->setupProcessing (setup) == kResultTrue; +} + +//------------------------------------------------------------------------ +void Vst2Wrapper::setSampleRate (float newSamplerate) +{ + if (processing) + return; + + if (newSamplerate != sampleRate) + { + AudioEffectX::setSampleRate (newSamplerate); + setupProcessing (); + } +} + +//------------------------------------------------------------------------ +void Vst2Wrapper::setBlockSize (VstInt32 newBlockSize) +{ + if (processing) + return; + + if (blockSize != newBlockSize) + { + AudioEffectX::setBlockSize (newBlockSize); + setupProcessing (); + } +} + +//------------------------------------------------------------------------ +float Vst2Wrapper::getParameter (VstInt32 index) +{ + if (!mController) + return 0.f; + + if (index < (int32)mParameterMap.size ()) + { + ParamID id = mParameterMap.at (index).vst3ID; + return (float)mController->getParamNormalized (id); + } + + return 0.f; +} + +//------------------------------------------------------------------------ +void Vst2Wrapper::setParameter (VstInt32 index, float value) +{ + if (!mController) + return; + + if (index < (int32)mParameterMap.size ()) + { + ParamID id = mParameterMap.at (index).vst3ID; + addParameterChange (id, (ParamValue)value, 0); + } +} + +//------------------------------------------------------------------------ +void Vst2Wrapper::addParameterChange (ParamID id, ParamValue value, int32 sampleOffset) +{ + mGuiTransfer.addChange (id, value, sampleOffset); + mInputTransfer.addChange (id, value, sampleOffset); +} + +//------------------------------------------------------------------------ +void Vst2Wrapper::setProgram (VstInt32 program) +{ + if (mProgramParameterID != kNoParamId && mController != 0 && mProgramParameterIdx != -1) + { + AudioEffectX::setProgram (program); + + ParameterInfo paramInfo = {0}; + if (mController->getParameterInfo (mProgramParameterIdx, paramInfo) == kResultTrue) + { + if (paramInfo.stepCount > 0 && program <= paramInfo.stepCount) + { + ParamValue normalized = (ParamValue)program / (ParamValue)paramInfo.stepCount; + addParameterChange (mProgramParameterID, normalized, 0); + } + } + } +} + +//------------------------------------------------------------------------ +void Vst2Wrapper::setProgramName (char* name) +{ + // not supported in VST 3 +} + +//------------------------------------------------------------------------ +void Vst2Wrapper::getProgramName (char* name) +{ + // name of the current program. Limited to #kVstMaxProgNameLen. + *name = 0; + if (mUnitInfo) + { + ProgramListInfo listInfo = {0}; + if (mUnitInfo->getProgramListInfo (0, listInfo) == kResultTrue) + { + String128 tmp = {0}; + if (mUnitInfo->getProgramName (listInfo.id, curProgram, tmp) == kResultTrue) + { + String str (tmp); + str.copyTo8 (name, 0, kVstMaxProgNameLen); + } + } + } +} + +//------------------------------------------------------------------------ +bool Vst2Wrapper::getProgramNameIndexed (VstInt32, VstInt32 index, char* name) +{ + *name = 0; + if (mUnitInfo) + { + ProgramListInfo listInfo = {0}; + if (mUnitInfo->getProgramListInfo (0, listInfo) == kResultTrue) + { + String128 tmp = {0}; + if (mUnitInfo->getProgramName (listInfo.id, index, tmp) == kResultTrue) + { + String str (tmp); + str.copyTo8 (name, 0, kVstMaxProgNameLen); + return true; + } + } + } + + return false; +} + +//------------------------------------------------------------------------ +void Vst2Wrapper::getParameterLabel (VstInt32 index, char* label) +{ + // units in which parameter \e index is displayed (i.e. "sec", "dB", "type", etc...). Limited to + // #kVstMaxParamStrLen. + *label = 0; + if (mController) + { + int32 vst3Index = mParameterMap.at (index).vst3Index; + + ParameterInfo paramInfo = {0}; + if (mController->getParameterInfo (vst3Index, paramInfo) == kResultTrue) + { + String str (paramInfo.units); + str.copyTo8 (label, 0, kVstMaxParamStrLen); + } + } +} + +//------------------------------------------------------------------------ +/*! Usually VST 2 hosts call setParameter (...) and getParameterDisplay (...) synchronously. + In setParameter (...) param changes get queued (guiTransfer) and transfered in idle (::onTimer). + The ::onTimer call almost always comes AFTER getParameterDisplay (...) and therefore returns an + old + value. To avoid sending back old values, getLastParamChange (...) returns the latest value + from the guiTransfer queue. */ +//------------------------------------------------------------------------ +bool Vst2Wrapper::getLastParamChange (ParamID id, ParamValue& value) +{ + ParameterChanges changes; + mGuiTransfer.transferChangesTo (changes); + for (int32 i = 0; i < changes.getParameterCount (); ++i) + { + IParamValueQueue* queue = changes.getParameterData (i); + if (queue) + { + if (queue->getParameterId () == id) + { + int32 points = queue->getPointCount (); + if (points > 0) + { + mGuiTransfer.transferChangesFrom (changes); + int32 sampleOffset = 0; + return queue->getPoint (points - 1, sampleOffset, value) == kResultTrue; + } + } + } + } + + mGuiTransfer.transferChangesFrom (changes); + return false; +} + +//------------------------------------------------------------------------ +void Vst2Wrapper::getParameterDisplay (VstInt32 index, char* text) +{ + // string representation ("0.5", "-3", "PLATE", etc...) of the value of parameter \e index. + // Limited to #kVstMaxParamStrLen. + *text = 0; + if (mController) + { + int32 vst3Index = mParameterMap.at (index).vst3Index; + + ParameterInfo paramInfo = {0}; + if (mController->getParameterInfo (vst3Index, paramInfo) == kResultTrue) + { + String128 tmp = {0}; + ParamValue value = 0; + if (!getLastParamChange (paramInfo.id, value)) + value = mController->getParamNormalized (paramInfo.id); + + if (mController->getParamStringByValue (paramInfo.id, value, tmp) == kResultTrue) + { + String str (tmp); + str.copyTo8 (text, 0, kVstMaxParamStrLen); + } + } + } +} + +//------------------------------------------------------------------------ +void Vst2Wrapper::getUnitPath (UnitID unitID, String& path) +{ + //! Build the unit path up to the root unit (e.g. "Modulators.LFO 1.". Separator is a ".") + for (int32 unitIndex = 0; unitIndex < mUnitInfo->getUnitCount (); ++unitIndex) + { + UnitInfo info = {0}; + mUnitInfo->getUnitInfo (unitIndex, info); + if (info.id == unitID) + { + String unitName (info.name); + unitName.append ("."); + path.insertAt (0, unitName); + if (info.parentUnitId != kRootUnitId) + getUnitPath (info.parentUnitId, path); + + break; + } + } +} +//------------------------------------------------------------------------ +void Vst2Wrapper::getParameterName (VstInt32 index, char* text) +{ + // name ("Time", "Gain", "RoomType", etc...) of parameter \e index. Limited to + // #kVstExtMaxParamStrLen. + *text = 0; + if (mController && index < (int32)mParameterMap.size ()) + { + int32 vst3Index = mParameterMap.at (index).vst3Index; + + ParameterInfo paramInfo = {0}; + if (mController->getParameterInfo (vst3Index, paramInfo) == kResultTrue) + { + String str; + if (vst2WrapperFullParameterPath) + { + //! The parameter's name contains the unit path (e.g. "LFO 1.freq") as well + if (mUnitInfo) + { + getUnitPath (paramInfo.unitId, str); + } + } + str.append (paramInfo.title); + + if (str.length () > kVstExtMaxParamStrLen) + { + //! In case the string's length exceeds the limit, try parameter's title without + // unit path. + str = paramInfo.title; + } + if (str.length () > kVstExtMaxParamStrLen) + { + str = paramInfo.shortTitle; + } + str.copyTo8 (text, 0, kVstExtMaxParamStrLen); + } + } +} + +//------------------------------------------------------------------------ +bool Vst2Wrapper::canParameterBeAutomated (VstInt32 index) +{ + if (mController && index < (int32)mParameterMap.size ()) + { + int32 vst3Index = mParameterMap.at (index).vst3Index; + + ParameterInfo paramInfo = {0}; + if (mController->getParameterInfo (vst3Index, paramInfo) == kResultTrue) + return (paramInfo.flags & ParameterInfo::kCanAutomate) != 0; + } + return false; +} + +//------------------------------------------------------------------------ +bool Vst2Wrapper::string2parameter (VstInt32 index, char* text) +{ + if (mController && index < (int32)mParameterMap.size ()) + { + int32 vst3Index = mParameterMap.at (index).vst3Index; + + ParameterInfo paramInfo = {0}; + if (mController->getParameterInfo (vst3Index, paramInfo) == kResultTrue) + { + TChar tString[1024] = {0}; + String tmp (text); + tmp.copyTo16 (tString, 0, 1023); + + ParamValue valueNormalized = 0.0; + + if (mController->getParamValueByString (paramInfo.id, tString, valueNormalized)) + { + setParameterAutomated (index, (float)valueNormalized); + // TODO: check if setParameterAutomated is correct + } + } + } + return false; +} + +//------------------------------------------------------------------------ +bool Vst2Wrapper::getParameterProperties (VstInt32 index, VstParameterProperties* p) +{ + if (mController && index < (int32)mParameterMap.size ()) + { + p->label[0] = 0; + p->shortLabel[0] = 0; + + int32 vst3Index = mParameterMap.at (index).vst3Index; + + ParameterInfo paramInfo = {0}; + if (mController->getParameterInfo (vst3Index, paramInfo) == kResultTrue) + { + String str (paramInfo.title); + str.copyTo8 (p->label, 0, kVstMaxLabelLen); + + String str2 (paramInfo.shortTitle); + str.copyTo8 (p->shortLabel, 0, kVstMaxShortLabelLen); + + if (paramInfo.stepCount == 0) // continuous + { + p->flags |= kVstParameterCanRamp; + } + else if (paramInfo.stepCount == 1) // on / off + { + p->flags |= kVstParameterIsSwitch; + } + else + { + p->minInteger = 0; + p->maxInteger = paramInfo.stepCount; + p->flags |= kVstParameterUsesIntegerMinMax; + } + + return true; + } + } + return false; +} + +//------------------------------------------------------------------------ +VstInt32 Vst2Wrapper::getChunk (void** data, bool isPreset) +{ + // Host stores Plug-in state. Returns the size in bytes of the chunk (Plug-in allocates the data + // array) + MemoryStream componentStream; + if (mComponent && mComponent->getState (&componentStream) != kResultTrue) + componentStream.setSize (0); + + MemoryStream controllerStream; + if (mController && mController->getState (&controllerStream) != kResultTrue) + controllerStream.setSize (0); + + if (componentStream.getSize () + controllerStream.getSize () == 0) + return 0; + + mChunk.setSize (0); + IBStreamer acc (&mChunk, kLittleEndian); + + acc.writeInt64 (componentStream.getSize ()); + acc.writeInt64 (controllerStream.getSize ()); + + acc.writeRaw (componentStream.getData (), (int32)componentStream.getSize ()); + acc.writeRaw (controllerStream.getData (), (int32)controllerStream.getSize ()); + + int32 chunkSize = (int32)mChunk.getSize (); + *data = mChunk.getData (); + return chunkSize; +} + +//------------------------------------------------------------------------ +VstInt32 Vst2Wrapper::setChunk (void* data, VstInt32 byteSize, bool isPreset) +{ + if (!mComponent) + return 0; + + // throw away all previously queued parameter changes, they are obsolete + mGuiTransfer.removeChanges (); + mInputTransfer.removeChanges (); + + MemoryStream chunk (data, byteSize); + IBStreamer acc (&chunk, kLittleEndian); + + int64 componentDataSize = 0; + int64 controllerDataSize = 0; + + acc.readInt64 (componentDataSize); + acc.readInt64 (controllerDataSize); + + VstPresetStream componentStream (((char*)data) + acc.tell (), componentDataSize); + VstPresetStream controllerStream (((char*)data) + acc.tell () + componentDataSize, + controllerDataSize); + + mComponent->setState (&componentStream); + componentStream.seek (0, IBStream::kIBSeekSet, 0); + + if (mController) + { + if (!isPreset) + { + if (Vst::IAttributeList* attr = componentStream.getAttributes ()) + attr->setString (Vst::PresetAttributes::kStateType, + String (Vst::StateType::kProject)); + if (Vst::IAttributeList* attr = controllerStream.getAttributes ()) + attr->setString (Vst::PresetAttributes::kStateType, + String (Vst::StateType::kProject)); + } + mController->setComponentState (&componentStream); + mController->setState (&controllerStream); + } + + return 0; +} + +//------------------------------------------------------------------------ +VstInt32 Vst2Wrapper::vst3ToVst2SpeakerArr (SpeakerArrangement vst3Arr) +{ + switch (vst3Arr) + { + case SpeakerArr::kMono: return kSpeakerArrMono; + case SpeakerArr::kStereo: return kSpeakerArrStereo; + case SpeakerArr::kStereoSurround: return kSpeakerArrStereoSurround; + case SpeakerArr::kStereoCenter: return kSpeakerArrStereoCenter; + case SpeakerArr::kStereoSide: return kSpeakerArrStereoSide; + case SpeakerArr::kStereoCLfe: return kSpeakerArrStereoCLfe; + case SpeakerArr::k30Cine: return kSpeakerArr30Cine; + case SpeakerArr::k30Music: return kSpeakerArr30Music; + case SpeakerArr::k31Cine: return kSpeakerArr31Cine; + case SpeakerArr::k31Music: return kSpeakerArr31Music; + case SpeakerArr::k40Cine: return kSpeakerArr40Cine; + case SpeakerArr::k40Music: return kSpeakerArr40Music; + case SpeakerArr::k41Cine: return kSpeakerArr41Cine; + case SpeakerArr::k41Music: return kSpeakerArr41Music; + case SpeakerArr::k50: return kSpeakerArr50; + case SpeakerArr::k51: return kSpeakerArr51; + case SpeakerArr::k60Cine: return kSpeakerArr60Cine; + case SpeakerArr::k60Music: return kSpeakerArr60Music; + case SpeakerArr::k61Cine: return kSpeakerArr61Cine; + case SpeakerArr::k61Music: return kSpeakerArr61Music; + case SpeakerArr::k70Cine: return kSpeakerArr70Cine; + case SpeakerArr::k70Music: return kSpeakerArr70Music; + case SpeakerArr::k71Cine: return kSpeakerArr71Cine; + case SpeakerArr::k71Music: return kSpeakerArr71Music; + case SpeakerArr::k80Cine: return kSpeakerArr80Cine; + case SpeakerArr::k80Music: return kSpeakerArr80Music; + case SpeakerArr::k81Cine: return kSpeakerArr81Cine; + case SpeakerArr::k81Music: return kSpeakerArr81Music; + case SpeakerArr::k102: return kSpeakerArr102; + } + + return kSpeakerArrUserDefined; +} + +//------------------------------------------------------------------------ +SpeakerArrangement Vst2Wrapper::vst2ToVst3SpeakerArr (VstInt32 vst2Arr) +{ + switch (vst2Arr) + { + case kSpeakerArrMono: return SpeakerArr::kMono; + case kSpeakerArrStereo: return SpeakerArr::kStereo; + case kSpeakerArrStereoSurround: return SpeakerArr::kStereoSurround; + case kSpeakerArrStereoCenter: return SpeakerArr::kStereoCenter; + case kSpeakerArrStereoSide: return SpeakerArr::kStereoSide; + case kSpeakerArrStereoCLfe: return SpeakerArr::kStereoCLfe; + case kSpeakerArr30Cine: return SpeakerArr::k30Cine; + case kSpeakerArr30Music: return SpeakerArr::k30Music; + case kSpeakerArr31Cine: return SpeakerArr::k31Cine; + case kSpeakerArr31Music: return SpeakerArr::k31Music; + case kSpeakerArr40Cine: return SpeakerArr::k40Cine; + case kSpeakerArr40Music: return SpeakerArr::k40Music; + case kSpeakerArr41Cine: return SpeakerArr::k41Cine; + case kSpeakerArr41Music: return SpeakerArr::k41Music; + case kSpeakerArr50: return SpeakerArr::k50; + case kSpeakerArr51: return SpeakerArr::k51; + case kSpeakerArr60Cine: return SpeakerArr::k60Cine; + case kSpeakerArr60Music: return SpeakerArr::k60Music; + case kSpeakerArr61Cine: return SpeakerArr::k61Cine; + case kSpeakerArr61Music: return SpeakerArr::k61Music; + case kSpeakerArr70Cine: return SpeakerArr::k70Cine; + case kSpeakerArr70Music: return SpeakerArr::k70Music; + case kSpeakerArr71Cine: return SpeakerArr::k71Cine; + case kSpeakerArr71Music: return SpeakerArr::k71Music; + case kSpeakerArr80Cine: return SpeakerArr::k80Cine; + case kSpeakerArr80Music: return SpeakerArr::k80Music; + case kSpeakerArr81Cine: return SpeakerArr::k81Cine; + case kSpeakerArr81Music: return SpeakerArr::k81Music; + case kSpeakerArr102: return SpeakerArr::k102; + } + + return 0; +} + +//------------------------------------------------------------------------ +VstInt32 Vst2Wrapper::vst3ToVst2Speaker (Vst::Speaker vst3Speaker) +{ + switch (vst3Speaker) + { + case Vst::kSpeakerM: return ::kSpeakerM; + case Vst::kSpeakerL: return ::kSpeakerL; + case Vst::kSpeakerR: return ::kSpeakerR; + case Vst::kSpeakerC: return ::kSpeakerC; + case Vst::kSpeakerLfe: return ::kSpeakerLfe; + case Vst::kSpeakerLs: return ::kSpeakerLs; + case Vst::kSpeakerRs: return ::kSpeakerRs; + case Vst::kSpeakerLc: return ::kSpeakerLc; + case Vst::kSpeakerRc: return ::kSpeakerRc; + case Vst::kSpeakerS: return ::kSpeakerS; + case Vst::kSpeakerSl: return ::kSpeakerSl; + case Vst::kSpeakerSr: return ::kSpeakerSr; + case Vst::kSpeakerTc: return ::kSpeakerTm; + case Vst::kSpeakerTfl: return ::kSpeakerTfl; + case Vst::kSpeakerTfc: return ::kSpeakerTfc; + case Vst::kSpeakerTfr: return ::kSpeakerTfr; + case Vst::kSpeakerTrl: return ::kSpeakerTrl; + case Vst::kSpeakerTrc: return ::kSpeakerTrc; + case Vst::kSpeakerTrr: return ::kSpeakerTrr; + case Vst::kSpeakerLfe2: return ::kSpeakerLfe2; + } + return ::kSpeakerUndefined; +} + +//------------------------------------------------------------------------ +static const char* gSpeakerNames[] = { + "M", ///< Mono (M) + "L", ///< Left (L) + "R", ///< Right (R) + "C", ///< Center (C) + "Lfe", ///< Subbass (Lfe) + "Ls", ///< Left Surround (Ls) + "Rs", ///< Right Surround (Rs) + "Lc", ///< Left of Center (Lc) + "Rc", ///< Right of Center (Rc) + "Cs", ///< Center of Surround (Cs) = Surround (S) + "Sl", ///< Side Left (Sl) + "Sr", ///< Side Right (Sr) + "Tm", ///< Top Middle (Tm) + "Tfl", ///< Top Front Left (Tfl) + "Tfc", ///< Top Front Center (Tfc) + "Tfr", ///< Top Front Right (Tfr) + "Trl", ///< Top Rear Left (Trl) + "Trc", ///< Top Rear Center (Trc) + "Trr", ///< Top Rear Right (Trr) + "Lfe2" ///< Subbass 2 (Lfe2) +}; +static const int32 kNumSpeakerNames = sizeof (gSpeakerNames) / sizeof (char*); + +//------------------------------------------------------------------------ +bool Vst2Wrapper::pinIndexToBusChannel (BusDirection dir, VstInt32 pinIndex, int32& busIndex, + int32& busChannel) +{ + AudioBusBuffers* busBuffers = dir == kInput ? mProcessData.inputs : mProcessData.outputs; + int32 busCount = dir == kInput ? mProcessData.numInputs : mProcessData.numOutputs; + uint64 mainBusFlags = dir == kInput ? mMainAudioInputBuses : mMainAudioOutputBuses; + + int32 sourceIndex = 0; + for (busIndex = 0; busIndex < busCount; busIndex++) + { + AudioBusBuffers& buffers = busBuffers[busIndex]; + if (mainBusFlags & (uint64 (1) << busIndex)) + { + for (busChannel = 0; busChannel < buffers.numChannels; busChannel++) + { + if (pinIndex == sourceIndex) + { + return true; + } + sourceIndex++; + } + } + } + return false; +} + +//------------------------------------------------------------------------ +bool Vst2Wrapper::getPinProperties (BusDirection dir, VstInt32 pinIndex, + VstPinProperties* properties) +{ + int32 busIndex = -1; + int32 busChannelIndex = -1; + + if (pinIndexToBusChannel (dir, pinIndex, busIndex, busChannelIndex)) + { + BusInfo busInfo = {0}; + if (mComponent && mComponent->getBusInfo (kAudio, dir, busIndex, busInfo) == kResultTrue) + { + properties->flags = kVstPinIsActive; // ???? + + String name (busInfo.name); + name.copyTo8 (properties->label, 0, kVstMaxLabelLen); + + if (busInfo.channelCount == 1) + { + properties->flags |= kVstPinUseSpeaker; + properties->arrangementType = kSpeakerArrMono; + } + if (busInfo.channelCount == 2) + { + properties->flags |= kVstPinUseSpeaker; + properties->flags |= kVstPinIsStereo; + properties->arrangementType = kSpeakerArrStereo; + } + else if (busInfo.channelCount > 2) + { + Vst::SpeakerArrangement arr = 0; + if (mProcessor && mProcessor->getBusArrangement (dir, busIndex, arr) == kResultTrue) + { + properties->flags |= kVstPinUseSpeaker; + properties->arrangementType = vst3ToVst2SpeakerArr (arr); + } + else + { + return false; + } + } + + return true; + } + } + + return false; +} + +//------------------------------------------------------------------------ +bool Vst2Wrapper::getInputProperties (VstInt32 index, VstPinProperties* properties) +{ + return getPinProperties (kInput, index, properties); +} + +//------------------------------------------------------------------------ +bool Vst2Wrapper::getOutputProperties (VstInt32 index, VstPinProperties* properties) +{ + return getPinProperties (kOutput, index, properties); +} + +//------------------------------------------------------------------------ +bool Vst2Wrapper::setSpeakerArrangement (VstSpeakerArrangement* pluginInput, + VstSpeakerArrangement* pluginOutput) +{ + if (!mProcessor || !mComponent) + return false; + + Vst::SpeakerArrangement newInputArr = 0; + Vst::SpeakerArrangement newOutputArr = 0; + Vst::SpeakerArrangement outputArr = 0; + Vst::SpeakerArrangement inputArr = 0; + + int32 inputBusCount = mComponent->getBusCount (kAudio, kInput); + int32 outputBusCount = mComponent->getBusCount (kAudio, kOutput); + + if (inputBusCount > 0) + if (mProcessor->getBusArrangement (kInput, 0, inputArr) != kResultTrue) + return false; + if (outputBusCount > 0) + if (mProcessor->getBusArrangement (kOutput, 0, outputArr) != kResultTrue) + return false; + + if (pluginInput) + { + newInputArr = vst2ToVst3SpeakerArr (pluginInput->type); + if (newInputArr == 0) + return false; + } + if (pluginOutput) + { + newOutputArr = vst2ToVst3SpeakerArr (pluginOutput->type); + if (newOutputArr == 0) + return false; + } + + if (newInputArr == 0) + newInputArr = inputArr; + if (newOutputArr == 0) + newOutputArr = outputArr; + + if (newInputArr != inputArr || newOutputArr != outputArr) + { + if (mProcessor->setBusArrangements ( + &newInputArr, (newInputArr > 0 && inputBusCount > 0) ? 1 : 0, &newOutputArr, + (newOutputArr > 0 && outputBusCount > 0) ? 1 : 0) != kResultTrue) + return false; + + restartComponent (kIoChanged); + } + + return true; +} + +//------------------------------------------------------------------------ +void Vst2Wrapper::setupVst2Arrangement (VstSpeakerArrangement*& vst2arr, + Vst::SpeakerArrangement vst3Arrangement) +{ + int32 numChannels = Vst::SpeakerArr::getChannelCount (vst3Arrangement); + + if (vst2arr && (numChannels == 0 || (numChannels > vst2arr->numChannels && numChannels > 8))) + { + free (vst2arr); + vst2arr = 0; + if (numChannels == 0) + return; + } + + if (vst2arr == 0) + { + int32 channelOverhead = numChannels > 8 ? numChannels - 8 : 0; + uint32 structSize = + sizeof (VstSpeakerArrangement) + channelOverhead * sizeof (VstSpeakerProperties); + vst2arr = (VstSpeakerArrangement*)malloc (structSize); + memset (vst2arr, 0, structSize); + } + + if (vst2arr) + { + vst2arr->type = vst3ToVst2SpeakerArr (vst3Arrangement); + vst2arr->numChannels = numChannels; + + Speaker vst3TestSpeaker = 1; + + for (int32 i = 0; i < numChannels; i++) + { + VstSpeakerProperties& props = vst2arr->speakers[i]; + + // find nextSpeaker in vst3 arrangement + Speaker vst3Speaker = 0; + while (vst3Speaker == 0 && vst3TestSpeaker != 0) + { + if (vst3Arrangement & vst3TestSpeaker) + vst3Speaker = vst3TestSpeaker; + + vst3TestSpeaker <<= 1; + } + + if (vst3Speaker) + { + props.type = vst3ToVst2Speaker (vst3Speaker); + if (props.type >= 0 && props.type < kNumSpeakerNames) + strncpy (props.name, gSpeakerNames[props.type], kVstMaxNameLen); + else + sprintf (props.name, "%d", i + 1); + } + } + } +} + +//------------------------------------------------------------------------ +bool Vst2Wrapper::getSpeakerArrangement (VstSpeakerArrangement** pluginInput, + VstSpeakerArrangement** pluginOutput) +{ + if (!mProcessor) + return false; + + Vst::SpeakerArrangement inputArr = 0; + Vst::SpeakerArrangement outputArr = 0; + + if (mProcessor->getBusArrangement (kInput, 0, inputArr) != kResultTrue) + inputArr = 0; + + if (mProcessor->getBusArrangement (kOutput, 0, outputArr) != kResultTrue) + outputArr = 0; + + setupVst2Arrangement (mVst2InputArrangement, inputArr); + setupVst2Arrangement (mVst2OutputArrangement, outputArr); + + *pluginInput = mVst2InputArrangement; + *pluginOutput = mVst2OutputArrangement; + + return mVst2InputArrangement != 0 && mVst2OutputArrangement != 0; +} + +//------------------------------------------------------------------------ +bool Vst2Wrapper::setBypass (bool onOff) +{ + if (mBypassParameterID != kNoParamId) + { + addParameterChange (mBypassParameterID, onOff ? 1.0 : 0.0, 0); + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +bool Vst2Wrapper::setProcessPrecision (VstInt32 precision) +{ + int32 newVst3SampleSize = -1; + + if (precision == kVstProcessPrecision32) + newVst3SampleSize = kSample32; + else if (precision == kVstProcessPrecision64) + newVst3SampleSize = kSample64; + + if (newVst3SampleSize != mVst3SampleSize) + { + if (mProcessor && mProcessor->canProcessSampleSize (newVst3SampleSize) == kResultTrue) + { + mVst3SampleSize = newVst3SampleSize; + setupProcessing (); + + setupBuses (); + + return true; + } + return false; + } + return true; +} + +//----------------------------------------------------------------------------- +VstInt32 Vst2Wrapper::getNumMidiInputChannels () +{ + if (!mComponent) + return 0; + + int32 busCount = mComponent->getBusCount (kEvent, kInput); + if (busCount > 0) + { + BusInfo busInfo = {0}; + if (mComponent->getBusInfo (kEvent, kInput, 0, busInfo) == kResultTrue) + { + return busInfo.channelCount; + } + } + return 0; +} + +//----------------------------------------------------------------------------- +VstInt32 Vst2Wrapper::getNumMidiOutputChannels () +{ + if (!mComponent) + return 0; + + int32 busCount = mComponent->getBusCount (kEvent, kOutput); + if (busCount > 0) + { + BusInfo busInfo = {0}; + if (mComponent->getBusInfo (kEvent, kOutput, 0, busInfo) == kResultTrue) + { + return busInfo.channelCount; + } + } + return 0; +} + +//----------------------------------------------------------------------------- +VstInt32 Vst2Wrapper::getGetTailSize () +{ + if (mProcessor) + return mProcessor->getTailSamples (); + + return 0; +} + +//----------------------------------------------------------------------------- +bool Vst2Wrapper::getEffectName (char* effectName) +{ + if (mName[0]) + { + strncpy (effectName, mName, kVstMaxEffectNameLen); + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +bool Vst2Wrapper::getVendorString (char* text) +{ + if (mVendor[0]) + { + strncpy (text, mVendor, kVstMaxVendorStrLen); + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +VstInt32 Vst2Wrapper::getVendorVersion () +{ + return mVersion; +} + +//----------------------------------------------------------------------------- +VstIntPtr Vst2Wrapper::vendorSpecific (VstInt32 lArg, VstIntPtr lArg2, void* ptrArg, float floatArg) +{ + switch (lArg) + { + case 'stCA': + case 'stCa': + switch (lArg2) + { + //--- ------- + case 'FUID': + if (ptrArg && mVst3EffectClassID.isValid ()) + { + memcpy ((char*)ptrArg, mVst3EffectClassID, 16); + return 1; + } + break; + //--- ------- + case 'Whee': + if (editor) + editor->onWheel (floatArg); + return 1; + break; + } + } + + return AudioEffectX::vendorSpecific (lArg, lArg2, ptrArg, floatArg); +} + +//----------------------------------------------------------------------------- +VstPlugCategory Vst2Wrapper::getPlugCategory () +{ + if (mSubCategories[0]) + { + if (strstr (mSubCategories, "Analyzer")) + return kPlugCategAnalysis; + + else if (strstr (mSubCategories, "Delay") || strstr (mSubCategories, "Reverb")) + return kPlugCategRoomFx; + + else if (strstr (mSubCategories, "Dynamics") || strstr (mSubCategories, "Mastering")) + return kPlugCategMastering; + + else if (strstr (mSubCategories, "Restoration")) + return kPlugCategRestoration; + + else if (strstr (mSubCategories, "Generator")) + return kPlugCategGenerator; + + else if (strstr (mSubCategories, "Spatial")) + return kPlugCategSpacializer; + + else if (strstr (mSubCategories, "Fx")) + return kPlugCategEffect; + + else if (strstr (mSubCategories, "Instrument")) + return kPlugCategSynth; + } + return kPlugCategUnknown; +} + +//----------------------------------------------------------------------------- +VstInt32 Vst2Wrapper::canDo (char* text) +{ + if (stricmp (text, "sendVstEvents") == 0) + { + return -1; + } + else if (stricmp (text, "sendVstMidiEvent") == 0) + { + return hasEventOutputBuses ? 1 : -1; + } + else if (stricmp (text, "receiveVstEvents") == 0) + { + return -1; + } + else if (stricmp (text, "receiveVstMidiEvent") == 0) + { + return hasEventInputBuses ? 1 : -1; + } + else if (stricmp (text, "receiveVstTimeInfo") == 0) + { + return 1; + } + else if (stricmp (text, "offline") == 0) + { + if (processing) + return 0; + if (mVst3processMode == kOffline) + return 1; + + bool canOffline = setupProcessing (kOffline); + setupProcessing (); + return canOffline ? 1 : -1; + } + else if (stricmp (text, "midiProgramNames") == 0) + { + if (mUnitInfo) + { + UnitID unitId = -1; + if (mUnitInfo->getUnitByBus (kEvent, kInput, 0, 0, unitId) == kResultTrue && unitId >= 0) + return 1; + } + return -1; + } + else if (stricmp (text, "bypass") == 0) + { + return mBypassParameterID != kNoParamId ? 1 : -1; + } + return 0; // do not know +} + +//----------------------------------------------------------------------------- +bool Vst2Wrapper::setupMidiProgram (int32 midiChannel, ProgramListID programListId, + MidiProgramName& midiProgramName) +{ + if (mUnitInfo) + { + String128 string128 = {0}; + + if (mUnitInfo->getProgramName (programListId, midiProgramName.thisProgramIndex, string128) == + kResultTrue) + { + String str (string128); + str.copyTo8 (midiProgramName.name, 0, kVstMaxNameLen); + + midiProgramName.midiProgram = midiProgramName.thisProgramIndex; + midiProgramName.midiBankMsb = -1; + midiProgramName.midiBankLsb = -1; + midiProgramName.parentCategoryIndex = -1; + midiProgramName.flags = 0; + + if (mUnitInfo->getProgramInfo (programListId, midiProgramName.thisProgramIndex, + PresetAttributes::kInstrument, string128) == kResultTrue) + { + midiProgramName.parentCategoryIndex = + lookupProgramCategory (midiChannel, string128); + } + return true; + } + } + return false; +} + +//----------------------------------------------------------------------------- +VstInt32 Vst2Wrapper::getMidiProgramName (VstInt32 channel, MidiProgramName* midiProgramName) +{ + UnitID unitId; + ProgramListID programListId; + if (mUnitInfo && getProgramListAndUnit (channel, unitId, programListId)) + { + if (midiProgramName) + setupMidiProgram (channel, programListId, *midiProgramName); + + ProgramListInfo programListInfo; + if (getProgramListInfoByProgramListID (programListId, programListInfo)) + return programListInfo.programCount; + } + return 0; +} + +//----------------------------------------------------------------------------- +VstInt32 Vst2Wrapper::getCurrentMidiProgram (VstInt32 channel, MidiProgramName* currentProgram) +{ + if (mUnitInfo && mController) + { + UnitID unitId; + ProgramListID programListId; + if (getProgramListAndUnit (channel, unitId, programListId)) + { + // find program selector parameter + int32 parameterCount = mController->getParameterCount (); + for (int32 i = 0; i < parameterCount; i++) + { + ParameterInfo parameterInfo = {0}; + if (mController->getParameterInfo (i, parameterInfo) == kResultTrue) + { + if ((parameterInfo.flags & ParameterInfo::kIsProgramChange) != 0 && + parameterInfo.unitId == unitId) + { + ParamValue normalized = mController->getParamNormalized (parameterInfo.id); + int32 discreteValue = + Min ((int32) (normalized * (parameterInfo.stepCount + 1)), + parameterInfo.stepCount); + + if (currentProgram) + { + currentProgram->thisProgramIndex = discreteValue; + setupMidiProgram (channel, programListId, *currentProgram); + } + + return discreteValue; + } + } + } + } + } + + return 0; +} + +//----------------------------------------------------------------------------- +VstInt32 Vst2Wrapper::getMidiProgramCategory (VstInt32 channel, MidiProgramCategory* category) +{ + // try to rebuild it each time + setupProgramCategories (); + + if (channel >= (VstInt32)mProgramCategories.size ()) + return 0; + + std::vector& channelCategories = mProgramCategories[channel]; + if (category && category->thisCategoryIndex < (VstInt32)channelCategories.size ()) + { + ProgramCategory& cat = channelCategories[category->thisCategoryIndex]; + if (cat.vst2Category.thisCategoryIndex == category->thisCategoryIndex) + memcpy (category, &cat.vst2Category, sizeof (MidiProgramCategory)); + } + return (VstInt32)channelCategories.size (); +} + +//----------------------------------------------------------------------------- +bool Vst2Wrapper::hasMidiProgramsChanged (VstInt32 channel) +{ + // names of programs or program categories have changed + return false; +} + +//----------------------------------------------------------------------------- +bool Vst2Wrapper::getMidiKeyName (VstInt32 channel, MidiKeyName* keyName) +{ + UnitID unitId; + ProgramListID programListId; + if (mUnitInfo && getProgramListAndUnit (channel, unitId, programListId)) + { + String128 string128 = {0}; + if (mUnitInfo->getProgramPitchName (programListId, keyName->thisProgramIndex, + keyName->thisKeyNumber, string128)) + { + String str (string128); + str.copyTo8 (keyName->keyName, 0, kVstMaxNameLen); + return true; + } + } + return false; +} + +//----------------------------------------------------------------------------- +bool Vst2Wrapper::getProgramListAndUnit (int32 midiChannel, UnitID& unitId, + ProgramListID& programListId) +{ + programListId = kNoProgramListId; + unitId = -1; + + // use the first input event bus (VST2 has only 1 bus for event) + if (mUnitInfo && mUnitInfo->getUnitByBus (kEvent, kInput, 0, midiChannel, unitId) == kResultTrue) + { + for (int32 i = 0, unitCount = mUnitInfo->getUnitCount (); i < unitCount; i++) + { + UnitInfo unitInfoStruct = {0}; + if (mUnitInfo->getUnitInfo (i, unitInfoStruct) == kResultTrue) + { + if (unitId == unitInfoStruct.id) + { + programListId = unitInfoStruct.programListId; + return programListId != kNoProgramListId; + } + } + } + } + + return false; +} + +//----------------------------------------------------------------------------- +bool Vst2Wrapper::getProgramListInfoByProgramListID (ProgramListID programListId, + ProgramListInfo& info) +{ + if (mUnitInfo) + { + int32 programListCount = mUnitInfo->getProgramListCount (); + for (int32 i = 0; i < programListCount; i++) + { + memset (&info, 0, sizeof (ProgramListInfo)); + if (mUnitInfo->getProgramListInfo (i, info) == kResultTrue) + { + if (info.id == programListId) + { + return true; + } + } + } + } + return false; +} + +//----------------------------------------------------------------------------- +int32 Vst2Wrapper::lookupProgramCategory (int32 midiChannel, String128 instrumentAttribute) +{ + std::vector& channelCategories = mProgramCategories[midiChannel]; + + for (uint32 categoryIndex = 0; categoryIndex < channelCategories.size (); categoryIndex++) + { + ProgramCategory& cat = channelCategories[categoryIndex]; + if (memcmp (instrumentAttribute, cat.vst3InstrumentAttribute, sizeof (String128)) == 0) + return categoryIndex; + } + + return -1; +} + +//----------------------------------------------------------------------------- +uint32 Vst2Wrapper::makeCategoriesRecursive (std::vector& channelCategories, + String128 vst3Category) +{ + for (uint32 categoryIndex = 0; categoryIndex < channelCategories.size (); categoryIndex++) + { + ProgramCategory& cat = channelCategories[categoryIndex]; + if (memcmp (vst3Category, cat.vst3InstrumentAttribute, sizeof (String128)) == 0) + { + return categoryIndex; + } + } + + int32 parentCategorIndex = -1; + + String128 str; + String strAcc (str); + strAcc.copyTo16 (vst3Category, 0, 127); + int32 len = strAcc.length (); + String singleName; + + char16 divider = '|'; + for (int32 strIndex = len - 1; strIndex >= 0; strIndex--) + { + bool isDivider = str[strIndex] == divider; + str[strIndex] = 0; // zero out rest + if (isDivider) + { + singleName.assign (vst3Category + strIndex + 1); + parentCategorIndex = makeCategoriesRecursive (channelCategories, str); + break; + } + } + + // make new + ProgramCategory cat = {0}; + memcpy (cat.vst3InstrumentAttribute, vst3Category, sizeof (String128)); + singleName.copyTo8 (cat.vst2Category.name, 0, kVstMaxNameLen); + cat.vst2Category.parentCategoryIndex = parentCategorIndex; + cat.vst2Category.thisCategoryIndex = (int32)channelCategories.size (); + + channelCategories.push_back (cat); + + return cat.vst2Category.thisCategoryIndex; +} + +//----------------------------------------------------------------------------- +void Vst2Wrapper::setupProgramCategories () +{ + mProgramCategories.clear (); + if (mUnitInfo && mComponent) + { + if (mComponent->getBusCount (kEvent, kInput) > 0) + { + for (int32 channel = 0; channel < 16; channel++) // the 16 channels + { + // make vector for channel + mProgramCategories.push_back (std::vector ()); + std::vector& channelCategories = mProgramCategories[channel]; + + // scan program list of channel and find categories + UnitID unitId; + ProgramListID programListId; + if (getProgramListAndUnit (channel, unitId, programListId)) + { + ProgramListInfo programListInfo; + if (getProgramListInfoByProgramListID (programListId, programListInfo)) + { + for (int32 programIndex = 0; programIndex < programListInfo.programCount; + programIndex++) + { + String128 string128 = {0}; + if (mUnitInfo->getProgramInfo (programListId, programIndex, + PresetAttributes::kInstrument, + string128) == kResultTrue) + { + makeCategoriesRecursive (channelCategories, string128); + } + } + } + } + } + } + } +} + +//----------------------------------------------------------------------------- +void Vst2Wrapper::setVendorName (char* name) +{ + memcpy (mVendor, name, sizeof (mVendor)); +} + +//----------------------------------------------------------------------------- +void Vst2Wrapper::setEffectName (char* effectName) +{ + memcpy (mName, effectName, sizeof (mName)); +} + +//----------------------------------------------------------------------------- +void Vst2Wrapper::setEffectVersion (char* version) +{ + if (!version) + mVersion = 0; + else + { + int32 major = 1; + int32 minor = 0; + int32 subminor = 0; + int32 subsubminor = 0; + int32 ret = sscanf (version, "%d.%d.%d.%d", &major, &minor, &subminor, &subsubminor); + mVersion = (major & 0xff) << 24; + if (ret > 3) + mVersion += (subsubminor & 0xff); + if (ret > 2) + mVersion += (subminor & 0xff) << 8; + if (ret > 1) + mVersion += (minor & 0xff) << 16; + } +} + +//----------------------------------------------------------------------------- +void Vst2Wrapper::setSubCategories (char* string) +{ + memcpy (mSubCategories, string, sizeof (mSubCategories)); + + if (strstr (mSubCategories, "Instrument")) + isSynth (true); +} + +//----------------------------------------------------------------------------- +void Vst2Wrapper::setupBuses () +{ + if (!mComponent) + return; + + mProcessData.prepare (*mComponent, 0, mVst3SampleSize); + + setNumInputs (countMainBusChannels (kInput, mMainAudioInputBuses)); + setNumOutputs (countMainBusChannels (kOutput, mMainAudioOutputBuses)); + + hasEventInputBuses = mComponent->getBusCount (kEvent, kInput) > 0; + hasEventOutputBuses = mComponent->getBusCount (kEvent, kOutput) > 0; + + if (hasEventInputBuses) + { + if (mInputEvents == 0) + mInputEvents = new EventList (kMaxEvents); + } + else + { + if (mInputEvents) + { + delete mInputEvents; + mInputEvents = nullptr; + } + } + + if (hasEventOutputBuses) + { + if (mOutputEvents == 0) + mOutputEvents = new EventList (kMaxEvents); + if (mVst2OutputEvents == 0) + mVst2OutputEvents = new Vst2MidiEventQueue (kMaxEvents); + } + else + { + if (mOutputEvents) + { + delete mOutputEvents; + mOutputEvents = nullptr; + } + if (mVst2OutputEvents) + { + delete mVst2OutputEvents; + mVst2OutputEvents = nullptr; + } + } +} + +//----------------------------------------------------------------------------- +void Vst2Wrapper::setupParameters () +{ + mParameterMap.clear (); + mParamIndexMap.clear (); + mBypassParameterID = mProgramParameterID = kNoParamId; + mProgramParameterIdx = -1; + + std::vector programParameterInfos; + std::vector programParameterIdxs; + + int32 paramCount = mController ? mController->getParameterCount () : 0; + int32 vst2ParamID = 0; + for (int32 i = 0; i < paramCount; i++) + { + ParameterInfo paramInfo = {0}; + if (mController->getParameterInfo (i, paramInfo) == kResultTrue) + { + //--- ------------------------------------------ + if ((paramInfo.flags & ParameterInfo::kIsBypass) != 0) + { + if (mBypassParameterID == kNoParamId) + mBypassParameterID = paramInfo.id; + } + //--- ------------------------------------------ + else if ((paramInfo.flags & ParameterInfo::kIsProgramChange) != 0) + { + programParameterInfos.push_back (paramInfo); + programParameterIdxs.push_back (i); + if (paramInfo.unitId == kRootUnitId) + { + if (mProgramParameterID == kNoParamId) + { + mProgramParameterID = paramInfo.id; + mProgramParameterIdx = i; + } + } + + if (gExportProgramChangeParameters == true) + { + ParamMapEntry entry = {paramInfo.id, i}; + mParameterMap.push_back (entry); + mParamIndexMap[paramInfo.id] = vst2ParamID; + vst2ParamID++; + } + } + //--- ------------------------------------------ + // do not export read only parameters to VST2 + else if ((paramInfo.flags & ParameterInfo::kIsReadOnly) == 0) + { + ParamMapEntry entry = {paramInfo.id, i}; + mParameterMap.push_back (entry); + mParamIndexMap[paramInfo.id] = vst2ParamID; + vst2ParamID++; + } + } + } + + numParams = cEffect.numParams = (int32)mParameterMap.size (); + + mInputTransfer.setMaxParameters (paramCount); + mOutputTransfer.setMaxParameters (paramCount); + mGuiTransfer.setMaxParameters (paramCount); + mInputChanges.setMaxParameters (paramCount); + mOutputChanges.setMaxParameters (paramCount); + + for (int32 midiChannel = 0; midiChannel < kMaxProgramChangeParameters; midiChannel++) + { + mProgramChangeParameterIDs[midiChannel] = kNoParamId; + mProgramChangeParameterIdxs[midiChannel] = -1; + + UnitID unitId; + ProgramListID programListId; + if (getProgramListAndUnit (midiChannel, unitId, programListId)) + { + for (int32 i = 0; i < (int32)programParameterInfos.size (); i++) + { + const ParameterInfo& paramInfo = programParameterInfos.at (i); + if (paramInfo.unitId == unitId) + { + mProgramChangeParameterIDs[midiChannel] = paramInfo.id; + mProgramChangeParameterIdxs[midiChannel] = programParameterIdxs.at (i); + break; + } + } + } + } +} + +//----------------------------------------------------------------------------- +void Vst2Wrapper::initMidiCtrlerAssignment () +{ + if (!mMidiMapping || !mComponent) + return; + + int32 busses = Min (mComponent->getBusCount (kEvent, kInput), kMaxMidiMappingBusses); + + if (!mMidiCCMapping[0][0]) + { + for (int32 b = 0; b < busses; b++) + for (int32 i = 0; i < 16; i++) + mMidiCCMapping[b][i] = NEW int32[Vst::kCountCtrlNumber]; + } + + ParamID paramID; + for (int32 b = 0; b < busses; b++) + { + for (int16 ch = 0; ch < 16; ch++) + { + for (int32 i = 0; i < Vst::kCountCtrlNumber; i++) + { + paramID = kNoParamId; + if (mMidiMapping->getMidiControllerAssignment (b, ch, (CtrlNumber)i, paramID) == + kResultTrue) + { + // TODO check if tag is associated to a parameter + mMidiCCMapping[b][ch][i] = paramID; + } + else + mMidiCCMapping[b][ch][i] = kNoParamId; + } + } + } +} + +//----------------------------------------------------------------------------- +int32 Vst2Wrapper::countMainBusChannels (BusDirection dir, uint64& mainBusBitset) +{ + int32 result = 0; + mainBusBitset = 0; + + int32 busCount = mComponent->getBusCount (kAudio, dir); + for (int32 i = 0; i < busCount; i++) + { + BusInfo busInfo = {0}; + if (mComponent->getBusInfo (kAudio, dir, i, busInfo) == kResultTrue) + { + if (busInfo.busType == kMain) + { + result += busInfo.channelCount; + mainBusBitset |= (uint64 (1) << i); + + mComponent->activateBus (kAudio, dir, i, true); + } + else if (busInfo.flags & BusInfo::kDefaultActive) + { + mComponent->activateBus (kAudio, dir, i, false); + } + } + } + return result; +} + +//------------------------------------------------------------------------ +const uint8 kNoteOff = 0x80; ///< note, off velocity +const uint8 kNoteOn = 0x90; ///< note, on velocity +const uint8 kPolyPressure = 0xA0; ///< note, pressure +const uint8 kController = 0xB0; ///< controller, value +const uint8 kProgramChangeStatus = 0xC0; ///< program change +const uint8 kAfterTouchStatus = 0xD0; ///< channel pressure +const uint8 kPitchBendStatus = 0xE0; ///< lsb, msb + +const float kMidiScaler = 1.f / 127.f; +static const uint8 kChannelMask = 0x0F; +static const uint8 kStatusMask = 0xF0; +static const uint32 kDataMask = 0x7F; + +//----------------------------------------------------------------------------- +void Vst2Wrapper::processMidiEvent (VstMidiEvent* midiEvent, int32 bus) +{ + Event toAdd = {bus, 0}; + uint8 status = midiEvent->midiData[0] & kStatusMask; + uint8 channel = midiEvent->midiData[0] & kChannelMask; + if (channel < 16) + { + //--- ----------------------------- + if (status == kNoteOn || status == kNoteOff) + { + toAdd.sampleOffset = midiEvent->deltaFrames; + if (midiEvent->flags & kVstMidiEventIsRealtime) + toAdd.flags |= Event::kIsLive; + + toAdd.ppqPosition = 0; + + if (status == kNoteOff) // note off + { + toAdd.type = Event::kNoteOffEvent; + toAdd.noteOff.channel = channel; + toAdd.noteOff.pitch = midiEvent->midiData[1]; + toAdd.noteOff.velocity = (float)midiEvent->noteOffVelocity * kMidiScaler; + toAdd.noteOff.noteId = -1; // TODO ? + } + else if (status == kNoteOn) // note on + { + toAdd.type = Event::kNoteOnEvent; + toAdd.noteOn.channel = channel; + toAdd.noteOn.pitch = midiEvent->midiData[1]; + toAdd.noteOn.tuning = midiEvent->detune; + toAdd.noteOn.velocity = (float)midiEvent->midiData[2] * kMidiScaler; + toAdd.noteOn.length = midiEvent->noteLength; + toAdd.noteOn.noteId = -1; // TODO ? + } + mInputEvents->addEvent (toAdd); + } + //--- ----------------------------- + else if (status == kPolyPressure) + { + toAdd.type = Vst::Event::kPolyPressureEvent; + + toAdd.sampleOffset = midiEvent->deltaFrames; + if (midiEvent->flags & kVstMidiEventIsRealtime) + toAdd.flags |= Event::kIsLive; + + toAdd.ppqPosition = 0; + toAdd.polyPressure.channel = channel; + toAdd.polyPressure.pitch = midiEvent->midiData[1] & kDataMask; + toAdd.polyPressure.pressure = (midiEvent->midiData[2] & kDataMask) * kMidiScaler; + toAdd.polyPressure.noteId = -1; // TODO ? + + mInputEvents->addEvent (toAdd); + } + //--- ----------------------------- + else if (status == kController) // controller + { + if (bus < kMaxMidiMappingBusses && mMidiCCMapping[bus][channel]) + { + ParamID paramID = mMidiCCMapping[bus][channel][midiEvent->midiData[1]]; + if (paramID != kNoParamId) + { + ParamValue value = (double)midiEvent->midiData[2] * kMidiScaler; + + int32 index = 0; + IParamValueQueue* queue = mInputChanges.addParameterData (paramID, index); + if (queue) + { + queue->addPoint (midiEvent->deltaFrames, value, index); + } + mGuiTransfer.addChange (paramID, value, midiEvent->deltaFrames); + } + } + } + //--- ----------------------------- + else if (status == kPitchBendStatus) + { + if (bus < kMaxMidiMappingBusses && mMidiCCMapping[bus][channel]) + { + ParamID paramID = mMidiCCMapping[bus][channel][Vst::kPitchBend]; + if (paramID != kNoParamId) + { + const double kPitchWheelScaler = 1. / (double)0x3FFF; + + const int32 ctrl = (midiEvent->midiData[1] & kDataMask) | + (midiEvent->midiData[2] & kDataMask) << 7; + ParamValue value = kPitchWheelScaler * (double)ctrl; + + int32 index = 0; + IParamValueQueue* queue = mInputChanges.addParameterData (paramID, index); + if (queue) + { + queue->addPoint (midiEvent->deltaFrames, value, index); + } + mGuiTransfer.addChange (paramID, value, midiEvent->deltaFrames); + } + } + } + //--- ----------------------------- + else if (status == kAfterTouchStatus) + { + if (bus < kMaxMidiMappingBusses && mMidiCCMapping[bus][channel]) + { + ParamID paramID = mMidiCCMapping[bus][channel][Vst::kAfterTouch]; + if (paramID != kNoParamId) + { + ParamValue value = + (ParamValue) (midiEvent->midiData[1] & kDataMask) * kMidiScaler; + + int32 index = 0; + IParamValueQueue* queue = mInputChanges.addParameterData (paramID, index); + if (queue) + { + queue->addPoint (midiEvent->deltaFrames, value, index); + } + mGuiTransfer.addChange (paramID, value, midiEvent->deltaFrames); + } + } + } + //--- ----------------------------- + else if (status == kProgramChangeStatus) + { + if (mProgramChangeParameterIDs[channel] != kNoParamId && + mProgramChangeParameterIdxs[channel] != -1) + { + ParameterInfo paramInfo = {0}; + if (mController->getParameterInfo (mProgramChangeParameterIdxs[channel], + paramInfo) == kResultTrue) + { + int32 program = midiEvent->midiData[1]; + if (paramInfo.stepCount > 0 && program <= paramInfo.stepCount) + { + ParamValue normalized = + (ParamValue)program / (ParamValue)paramInfo.stepCount; + addParameterChange (mProgramChangeParameterIDs[channel], normalized, + midiEvent->deltaFrames); + } + } + } + } + } +} + +//----------------------------------------------------------------------------- +VstInt32 Vst2Wrapper::processEvents (VstEvents* events) +{ + if (mInputEvents == 0) + return 0; + mInputEvents->clear (); + + for (int32 i = 0; i < events->numEvents; i++) + { + VstEvent* e = events->events[i]; + if (e->type == kVstMidiType) + { + VstMidiEvent* midiEvent = (VstMidiEvent*)e; + processMidiEvent (midiEvent, 0); + } + //--- ----------------------------- + else if (e->type == kVstSysExType) + { + Event toAdd = {0}; + VstMidiSysexEvent& src = *(VstMidiSysexEvent*)e; + toAdd.type = Event::kDataEvent; + toAdd.sampleOffset = e->deltaFrames; + toAdd.data.type = DataEvent::kMidiSysEx; + toAdd.data.size = src.dumpBytes; + toAdd.data.bytes = (uint8*)src.sysexDump; + mInputEvents->addEvent (toAdd); + } + } + + return 0; +} + +//----------------------------------------------------------------------------- +inline void Vst2Wrapper::processOutputEvents () +{ + if (mVst2OutputEvents && mOutputEvents && mOutputEvents->getEventCount () > 0) + { + mVst2OutputEvents->flush (); + + Event e = {0}; + for (int32 i = 0, total = mOutputEvents->getEventCount (); i < total; i++) + { + if (mOutputEvents->getEvent (i, e) != kResultOk) + break; + + //---SysExclusif---------------- + if (e.type == Event::kDataEvent && e.data.type == DataEvent::kMidiSysEx) + { + VstMidiSysexEvent sysexEvent = {0}; + sysexEvent.deltaFrames = e.sampleOffset; + sysexEvent.dumpBytes = e.data.size; + sysexEvent.sysexDump = (char*)e.data.bytes; + + if (!mVst2OutputEvents->add (sysexEvent)) + break; + } + else + { + VstMidiEvent midiEvent = {0}; + midiEvent.deltaFrames = e.sampleOffset; + if (e.flags & Event::kIsLive) + midiEvent.flags = kVstMidiEventIsRealtime; + char* midiData = midiEvent.midiData; + + switch (e.type) + { + //--- --------------------- + case Event::kNoteOnEvent: + midiData[0] = (char)(kNoteOn | (e.noteOn.channel & kChannelMask)); + midiData[1] = (char)(e.noteOn.pitch & kDataMask); + midiData[2] = + (char)((int32) (e.noteOn.velocity * 127.f + 0.4999999f) & kDataMask); + if (midiData[2] == 0) // zero velocity => note off + midiData[0] = (char)(kNoteOff | (e.noteOn.channel & kChannelMask)); + midiEvent.detune = (char)e.noteOn.tuning; + midiEvent.noteLength = e.noteOn.length; + break; + + //--- --------------------- + case Event::kNoteOffEvent: + midiData[0] = (char)(kNoteOff | (e.noteOff.channel & kChannelMask)); + midiData[1] = (char)(e.noteOff.pitch & kDataMask); + midiData[2] = midiEvent.noteOffVelocity = + (char)((int32) (e.noteOff.velocity * 127.f + 0.4999999f) & kDataMask); + break; + + break; + } + + if (!mVst2OutputEvents->add (midiEvent)) + break; + } + } + + mOutputEvents->clear (); + + sendVstEventsToHost (*mVst2OutputEvents); + } +} + +//----------------------------------------------------------------------------- +inline void Vst2Wrapper::setupProcessTimeInfo () +{ + VstTimeInfo* vst2timeInfo = getTimeInfo (0xffffffff); + if (vst2timeInfo) + { + const uint32 portableFlags = + ProcessContext::kPlaying | ProcessContext::kCycleActive | ProcessContext::kRecording | + ProcessContext::kSystemTimeValid | ProcessContext::kProjectTimeMusicValid | + ProcessContext::kBarPositionValid | ProcessContext::kCycleValid | + ProcessContext::kTempoValid | ProcessContext::kTimeSigValid | + ProcessContext::kSmpteValid | ProcessContext::kClockValid; + + mProcessContext.state = ((uint32)vst2timeInfo->flags) & portableFlags; + mProcessContext.sampleRate = vst2timeInfo->sampleRate; + mProcessContext.projectTimeSamples = (TSamples)vst2timeInfo->samplePos; + + if (mProcessContext.state & ProcessContext::kSystemTimeValid) + mProcessContext.systemTime = (TSamples)vst2timeInfo->nanoSeconds; + else + mProcessContext.systemTime = 0; + + if (mProcessContext.state & ProcessContext::kProjectTimeMusicValid) + mProcessContext.projectTimeMusic = vst2timeInfo->ppqPos; + else + mProcessContext.projectTimeMusic = 0; + + if (mProcessContext.state & ProcessContext::kBarPositionValid) + mProcessContext.barPositionMusic = vst2timeInfo->barStartPos; + else + mProcessContext.barPositionMusic = 0; + + if (mProcessContext.state & ProcessContext::kCycleValid) + { + mProcessContext.cycleStartMusic = vst2timeInfo->cycleStartPos; + mProcessContext.cycleEndMusic = vst2timeInfo->cycleEndPos; + } + else + mProcessContext.cycleStartMusic = mProcessContext.cycleEndMusic = 0.0; + + if (mProcessContext.state & ProcessContext::kTempoValid) + mProcessContext.tempo = vst2timeInfo->tempo; + else + mProcessContext.tempo = 120.0; + + if (mProcessContext.state & ProcessContext::kTimeSigValid) + { + mProcessContext.timeSigNumerator = vst2timeInfo->timeSigNumerator; + mProcessContext.timeSigDenominator = vst2timeInfo->timeSigDenominator; + } + else + mProcessContext.timeSigNumerator = mProcessContext.timeSigDenominator = 4; + + mProcessContext.frameRate.flags = 0; + if (mProcessContext.state & ProcessContext::kSmpteValid) + { + mProcessContext.smpteOffsetSubframes = vst2timeInfo->smpteOffset; + switch (vst2timeInfo->smpteFrameRate) + { + case kVstSmpte24fps: ///< 24 fps + mProcessContext.frameRate.framesPerSecond = 24; + break; + case kVstSmpte25fps: ///< 25 fps + mProcessContext.frameRate.framesPerSecond = 25; + break; + case kVstSmpte2997fps: ///< 29.97 fps + mProcessContext.frameRate.framesPerSecond = 30; + mProcessContext.frameRate.flags = FrameRate::kPullDownRate; + break; + case kVstSmpte30fps: ///< 30 fps + mProcessContext.frameRate.framesPerSecond = 30; + break; + case kVstSmpte2997dfps: ///< 29.97 drop + mProcessContext.frameRate.framesPerSecond = 30; + mProcessContext.frameRate.flags = + FrameRate::kPullDownRate | FrameRate::kDropRate; + break; + case kVstSmpte30dfps: ///< 30 drop + mProcessContext.frameRate.framesPerSecond = 30; + mProcessContext.frameRate.flags = FrameRate::kDropRate; + break; + case kVstSmpteFilm16mm: // not a smpte rate + case kVstSmpteFilm35mm: + mProcessContext.state &= ~ProcessContext::kSmpteValid; + break; + case kVstSmpte239fps: ///< 23.9 fps + mProcessContext.frameRate.framesPerSecond = 24; + mProcessContext.frameRate.flags = FrameRate::kPullDownRate; + break; + case kVstSmpte249fps: ///< 24.9 fps + mProcessContext.frameRate.framesPerSecond = 25; + mProcessContext.frameRate.flags = FrameRate::kPullDownRate; + break; + case kVstSmpte599fps: ///< 59.9 fps + mProcessContext.frameRate.framesPerSecond = 60; + mProcessContext.frameRate.flags = FrameRate::kPullDownRate; + break; + case kVstSmpte60fps: ///< 60 fps + mProcessContext.frameRate.framesPerSecond = 60; + break; + default: mProcessContext.state &= ~ProcessContext::kSmpteValid; break; + } + } + else + { + mProcessContext.smpteOffsetSubframes = 0; + mProcessContext.frameRate.framesPerSecond = 0; + } + + ///< MIDI Clock Resolution (24 Per Quarter Note), can be negative (nearest) + if (mProcessContext.state & ProcessContext::kClockValid) + mProcessContext.samplesToNextClock = vst2timeInfo->samplesToNextClock; + else + mProcessContext.samplesToNextClock = 0; + + mProcessData.processContext = &mProcessContext; + } + else + mProcessData.processContext = nullptr; +} + +//----------------------------------------------------------------------------- +template +inline void Vst2Wrapper::setProcessingBuffers (T** inputs, T** outputs) +{ + // set processing buffers + int32 sourceIndex = 0; + for (int32 i = 0; i < mProcessData.numInputs; i++) + { + AudioBusBuffers& buffers = mProcessData.inputs[i]; + if (mMainAudioInputBuses & (uint64 (1) << i)) + { + for (int32 j = 0; j < buffers.numChannels; j++) + { + buffers.channelBuffers32[j] = (Sample32*)inputs[sourceIndex++]; + } + } + else + buffers.silenceFlags = HostProcessData::kAllChannelsSilent; + } + + sourceIndex = 0; + for (int32 i = 0; i < mProcessData.numOutputs; i++) + { + AudioBusBuffers& buffers = mProcessData.outputs[i]; + buffers.silenceFlags = 0; + if (mMainAudioOutputBuses & (uint64 (1) << i)) + { + for (int32 j = 0; j < buffers.numChannels; j++) + { + buffers.channelBuffers32[j] = (Sample32*)outputs[sourceIndex++]; + } + } + } +} + +//----------------------------------------------------------------------------- +inline void Vst2Wrapper::setEventPPQPositions () +{ + if (!mInputEvents) + return; + + int32 eventCount = mInputEvents->getEventCount (); + if (eventCount > 0 && (mProcessContext.state & ProcessContext::kTempoValid) && + (mProcessContext.state & ProcessContext::kProjectTimeMusicValid)) + { + TQuarterNotes projectTimeMusic = mProcessContext.projectTimeMusic; + double secondsToQuarterNoteScaler = mProcessContext.tempo / 60.0; + double multiplicator = secondsToQuarterNoteScaler / this->sampleRate; + + for (int32 i = 0; i < eventCount; i++) + { + Event* e = mInputEvents->getEventByIndex (i); + if (e) + { + TQuarterNotes localTimeMusic = e->sampleOffset * multiplicator; + e->ppqPosition = projectTimeMusic + localTimeMusic; + } + } + } +} + +//----------------------------------------------------------------------------- +inline void Vst2Wrapper::doProcess (VstInt32 sampleFrames) +{ + if (!mProcessor) + return; + + mProcessData.numSamples = sampleFrames; + + if (processing == false) + startProcess (); + + mProcessData.inputEvents = mInputEvents; + mProcessData.outputEvents = mOutputEvents; + + setupProcessTimeInfo (); + setEventPPQPositions (); + + mInputTransfer.transferChangesTo (mInputChanges); + + mProcessData.inputParameterChanges = &mInputChanges; + mProcessData.outputParameterChanges = &mOutputChanges; + mOutputChanges.clearQueue (); + + // VST 3 process call + mProcessor->process (mProcessData); + + mOutputTransfer.transferChangesFrom (mOutputChanges); + processOutputEvents (); + + // clear input parameters and events + mInputChanges.clearQueue (); + if (mInputEvents) + mInputEvents->clear (); +} + +//----------------------------------------------------------------------------- +void Vst2Wrapper::processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames) +{ + if (mProcessData.symbolicSampleSize != kSample32) + return; + + setProcessingBuffers (inputs, outputs); + doProcess (sampleFrames); +} + +//----------------------------------------------------------------------------- +void Vst2Wrapper::processDoubleReplacing (double** inputs, double** outputs, VstInt32 sampleFrames) +{ + if (mProcessData.symbolicSampleSize != kSample64) + return; + + setProcessingBuffers (inputs, outputs); + doProcess (sampleFrames); +} + +//----------------------------------------------------------------------------- +void Vst2Wrapper::onTimer (Timer*) +{ + if (!mController) + return; + + ParamID id; + ParamValue value; + int32 sampleOffset; + + while (mOutputTransfer.getNextChange (id, value, sampleOffset)) + { + mController->setParamNormalized (id, value); + } + while (mGuiTransfer.getNextChange (id, value, sampleOffset)) + { + mController->setParamNormalized (id, value); + } +} + +//----------------------------------------------------------------------------- +// static +//----------------------------------------------------------------------------- +AudioEffect* Vst2Wrapper::create (IPluginFactory* factory, const TUID vst3ComponentID, + VstInt32 vst2ID, audioMasterCallback audioMaster) +{ + if (!factory) + return 0; + + IAudioProcessor* processor = nullptr; + FReleaser factoryReleaser (factory); + + factory->createInstance (vst3ComponentID, IAudioProcessor::iid, (void**)&processor); + if (processor) + { + IEditController* controller = nullptr; + if (processor->queryInterface (IEditController::iid, (void**)&controller) != kResultTrue) + { + FUnknownPtr component (processor); + if (component) + { + FUID editorCID; + if (component->getControllerClassId (editorCID) == kResultTrue) + { + factory->createInstance (editorCID, IEditController::iid, (void**)&controller); + } + } + } + + Vst2Wrapper* wrapper = + new Vst2Wrapper (processor, controller, audioMaster, vst3ComponentID, vst2ID, factory); + if (wrapper->init () == false) + { + delete wrapper; + return 0; + } + + FUnknownPtr factory2 (factory); + if (factory2) + { + PFactoryInfo factoryInfo; + if (factory2->getFactoryInfo (&factoryInfo) == kResultTrue) + wrapper->setVendorName (factoryInfo.vendor); + + for (int32 i = 0; i < factory2->countClasses (); i++) + { + Steinberg::PClassInfo2 classInfo2; + if (factory2->getClassInfo2 (i, &classInfo2) == Steinberg::kResultTrue) + { + if (memcmp (classInfo2.cid, vst3ComponentID, sizeof (TUID)) == 0) + { + wrapper->setSubCategories (classInfo2.subCategories); + wrapper->setEffectName (classInfo2.name); + wrapper->setEffectVersion (classInfo2.version); + + if (classInfo2.vendor[0] != 0) + wrapper->setVendorName (classInfo2.vendor); + + break; + } + } + } + } + + return wrapper; + } + + return 0; +} + +// FUnknown +//----------------------------------------------------------------------------- +tresult PLUGIN_API Vst2Wrapper::queryInterface (const char* iid, void** obj) +{ + QUERY_INTERFACE (iid, obj, FUnknown::iid, Vst::IHostApplication) + QUERY_INTERFACE (iid, obj, Vst::IHostApplication::iid, Vst::IHostApplication) + QUERY_INTERFACE (iid, obj, Vst::IComponentHandler::iid, Vst::IComponentHandler) + QUERY_INTERFACE (iid, obj, Vst::IUnitHandler::iid, Vst::IUnitHandler) + + // we are a VST 3 to VST 2 Wrapper + QUERY_INTERFACE (iid, obj, Vst::IVst3ToVst2Wrapper::iid, Vst::IVst3ToVst2Wrapper) + + *obj = 0; + return kNoInterface; +} + +//----------------------------------------------------------------------------- +// IHostApplication +//----------------------------------------------------------------------------- +tresult PLUGIN_API Vst2Wrapper::createInstance (TUID cid, TUID iid, void** obj) +{ + FUID classID (cid); + FUID interfaceID (iid); + if (classID == IMessage::iid && interfaceID == IMessage::iid) + { + *obj = new HostMessage; + return kResultTrue; + } + else if (classID == IAttributeList::iid && interfaceID == IAttributeList::iid) + { + *obj = new HostAttributeList; + return kResultTrue; + } + *obj = 0; + return kResultFalse; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API Vst2Wrapper::getName (String128 name) +{ + char8 productString[128]; + if (getHostProductString (productString)) + { + String str (productString); + str.copyTo16 (name, 0, 127); + + return kResultTrue; + } + return kResultFalse; +} + +//----------------------------------------------------------------------------- +// IComponentHandler +//----------------------------------------------------------------------------- +tresult PLUGIN_API Vst2Wrapper::beginEdit (ParamID tag) +{ + std::map::const_iterator iter = mParamIndexMap.find (tag); + if (iter != mParamIndexMap.end ()) + AudioEffectX::beginEdit ((*iter).second); + return kResultTrue; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API Vst2Wrapper::performEdit (ParamID tag, ParamValue valueNormalized) +{ + std::map::const_iterator iter = mParamIndexMap.find (tag); + if (iter != mParamIndexMap.end () && audioMaster) + audioMaster (&cEffect, audioMasterAutomate, (*iter).second, 0, 0, + (float)valueNormalized); // value is in opt + + mInputTransfer.addChange (tag, valueNormalized, 0); + + return kResultTrue; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API Vst2Wrapper::endEdit (ParamID tag) +{ + std::map::const_iterator iter = mParamIndexMap.find (tag); + if (iter != mParamIndexMap.end ()) + AudioEffectX::endEdit ((*iter).second); + return kResultTrue; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API Vst2Wrapper::restartComponent (int32 flags) +{ + tresult result = kResultFalse; + + //--- ---------------------- + if (flags & kIoChanged) + { + setupBuses (); + ioChanged (); + result = kResultTrue; + } + + //--- ---------------------- + if ((flags & kParamValuesChanged) || (flags & kParamTitlesChanged)) + { + updateDisplay (); + result = kResultTrue; + } + + //--- ---------------------- + if (flags & kLatencyChanged) + { + if (mProcessor) + setInitialDelay (mProcessor->getLatencySamples ()); + + ioChanged (); + result = kResultTrue; + } + + //--- ---------------------- + if (flags & kMidiCCAssignmentChanged) + { + initMidiCtrlerAssignment (); + result = kResultTrue; + } + + // kReloadComponent is Not supported in VST 2 + + return result; +} + +//----------------------------------------------------------------------------- +// IUnitHandler +//----------------------------------------------------------------------------- +tresult PLUGIN_API Vst2Wrapper::notifyUnitSelection (UnitID unitId) +{ + return kResultTrue; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API Vst2Wrapper::notifyProgramListChange (ProgramListID listId, int32 programIndex) +{ + // TODO -> redirect to hasMidiProgramsChanged somehow... + return kResultTrue; +} + +//----------------------------------------------------------------------------- +} // namespace Vst +} // namespace Steinberg + +extern bool InitModule (); + +//----------------------------------------------------------------------------- +extern "C" { + +#if defined(__GNUC__) && ((__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1))) +#define VST_EXPORT __attribute__ ((visibility ("default"))) +#elif WINDOWS +#define VST_EXPORT __declspec( dllexport ) +#else +#define VST_EXPORT +#endif + +//----------------------------------------------------------------------------- +/** Prototype of the export function main */ +//----------------------------------------------------------------------------- +VST_EXPORT AEffect* VSTPluginMain (audioMasterCallback audioMaster) +{ + // Get VST Version of the Host + if (!audioMaster (0, audioMasterVersion, 0, 0, 0, 0)) + return 0; // old version + + if (InitModule () == false) + return 0; + + // Create the AudioEffect + AudioEffect* effect = createEffectInstance (audioMaster); + if (!effect) + return 0; + + // Return the VST AEffect structure + return effect->getAeffect (); +} + +//----------------------------------------------------------------------------- +// support for old hosts not looking for VSTPluginMain +#if (TARGET_API_MAC_CARBON && __ppc__) +VST_EXPORT AEffect* main_macho (audioMasterCallback audioMaster) +{ + return VSTPluginMain (audioMaster); +} +#elif WIN32 +VST_EXPORT AEffect* MAIN (audioMasterCallback audioMaster) +{ + return VSTPluginMain (audioMaster); +} +#elif BEOS +VST_EXPORT AEffect* main_plugin (audioMasterCallback audioMaster) +{ + return VSTPluginMain (audioMaster); +} +#endif + +} // extern "C" +//----------------------------------------------------------------------------- + +/// \endcond diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vst2wrapper/vst2wrapper.h b/source/includes/vst3sdk/public.sdk/source/vst/vst2wrapper/vst2wrapper.h new file mode 100644 index 000000000..fe7da36c2 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vst2wrapper/vst2wrapper.h @@ -0,0 +1,335 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vst2wrapper/vst2wrapper.h +// Created by : Steinberg, 01/2009 +// Description : VST 3 -> VST 2 Wrapper +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +/** +************************************** +\page vst2xwrapper VST 2.x Wrapper +*************************** +\section VST2Introduction Introduction +*************************** +The VST 3 SDK comes with a helper class which wraps one VST 3 Audio Processor and Edit Controller to +a VST 2.x Plug-in. +\n\n +*************************** +\section VST2howdoesitwork How does it work ? +*************************** +You just need to add public.sdk/source/vst/vst2wrapper/vst2wrapper.sdk.cpp to your project and add +the following code somewhere in your sources: +\code + +#include "public.sdk/source/vst/vst2wrapper/vst2wrapper.h" + +//------------------------------------------------------------------------ +::AudioEffect* createEffectInstance (audioMasterCallback audioMaster) +{ + return Steinberg::Vst::Vst2Wrapper::create (GetPluginFactory (), kAudioProcessorCID, kVst2UniqueID, audioMaster); +} + +\endcode + */ +/// \cond ignore + +#pragma once + +#include "pluginterfaces/base/ftypes.h" +#include "pluginterfaces/vst/ivstaudioprocessor.h" +#include "pluginterfaces/vst/ivsteditcontroller.h" +#include "pluginterfaces/vst/ivsthostapplication.h" +#include "pluginterfaces/vst/ivstprocesscontext.h" +#include "pluginterfaces/vst/ivstunits.h" + +#include "public.sdk/source/common/memorystream.h" +#include "public.sdk/source/vst/hosting/eventlist.h" +#include "public.sdk/source/vst/hosting/parameterchanges.h" +#include "public.sdk/source/vst/hosting/processdata.h" +#include "public.sdk/source/vst2.x/audioeffectx.h" + +#include "base/source/fstring.h" +#include "base/source/timer.h" + +#include +#include + +//------------------------------------------------------------------------ +namespace Steinberg { +namespace Vst { + +class Vst2MidiEventQueue; + +//------------------------------------------------------------------------------------------------------- +class Vst2Wrapper : public ::AudioEffectX, + public IHostApplication, + public IComponentHandler, + public IUnitHandler, + public ITimerCallback, + public IVst3ToVst2Wrapper +{ +public: + //--- ------------------------------------------------------------------------------------------------- + // static creation method + static AudioEffect* create (IPluginFactory* factory, const TUID vst3ComponentID, + VstInt32 vst2ID, audioMasterCallback audioMaster); + + Vst2Wrapper (IAudioProcessor* processor, IEditController* controller, + audioMasterCallback audioMaster, const TUID vst3ComponentID, VstInt32 vst2ID, + IPluginFactory* factory = 0); + ~Vst2Wrapper (); + + bool init (); + + // AudioEffectX overrides ----------------------------------------------- + virtual void suspend () SMTG_OVERRIDE; // Called when Plug-in is switched to off + virtual void resume () SMTG_OVERRIDE; // Called when Plug-in is switched to on + virtual VstInt32 startProcess () SMTG_OVERRIDE; + virtual VstInt32 stopProcess () SMTG_OVERRIDE; + + virtual void setSampleRate (float newSamplerate) + SMTG_OVERRIDE; // Called when the sample rate changes (always in a suspend state) + virtual void setBlockSize (VstInt32 newBlockSize) + SMTG_OVERRIDE; // Called when the maximum block size changes + // (always in a suspend state). Note that the + // sampleFrames in Process Calls could be + // smaller than this block size, but NOT bigger. + + virtual float getParameter (VstInt32 index) SMTG_OVERRIDE; + virtual void setParameter (VstInt32 index, float value) SMTG_OVERRIDE; + + virtual void setProgram (VstInt32 program) SMTG_OVERRIDE; + virtual void setProgramName (char* name) SMTG_OVERRIDE; + virtual void getProgramName (char* name) SMTG_OVERRIDE; + virtual bool getProgramNameIndexed (VstInt32 category, VstInt32 index, + char* text) SMTG_OVERRIDE; + + virtual void getParameterLabel (VstInt32 index, char* label) SMTG_OVERRIDE; + virtual void getParameterDisplay (VstInt32 index, char* text) SMTG_OVERRIDE; + virtual void getParameterName (VstInt32 index, char* text) SMTG_OVERRIDE; + virtual bool canParameterBeAutomated (VstInt32 index) SMTG_OVERRIDE; + virtual bool string2parameter (VstInt32 index, char* text) SMTG_OVERRIDE; + virtual bool getParameterProperties (VstInt32 index, VstParameterProperties* p) SMTG_OVERRIDE; + + virtual VstInt32 getChunk (void** data, bool isPreset = false) SMTG_OVERRIDE; + virtual VstInt32 setChunk (void* data, VstInt32 byteSize, bool isPreset = false) SMTG_OVERRIDE; + + virtual bool getInputProperties (VstInt32 index, VstPinProperties* properties) SMTG_OVERRIDE; + virtual bool getOutputProperties (VstInt32 index, VstPinProperties* properties) SMTG_OVERRIDE; + virtual bool setSpeakerArrangement (VstSpeakerArrangement* pluginInput, + VstSpeakerArrangement* pluginOutput) SMTG_OVERRIDE; + virtual bool getSpeakerArrangement (VstSpeakerArrangement** pluginInput, + VstSpeakerArrangement** pluginOutput) SMTG_OVERRIDE; + virtual bool setBypass (bool onOff) SMTG_OVERRIDE; + + virtual bool setProcessPrecision (VstInt32 precision) SMTG_OVERRIDE; + virtual VstInt32 getNumMidiInputChannels () SMTG_OVERRIDE; + virtual VstInt32 getNumMidiOutputChannels () SMTG_OVERRIDE; + virtual VstInt32 getGetTailSize () SMTG_OVERRIDE; + virtual bool getEffectName (char* name) SMTG_OVERRIDE; + virtual bool getVendorString (char* text) SMTG_OVERRIDE; + virtual VstInt32 getVendorVersion () SMTG_OVERRIDE; + virtual VstIntPtr vendorSpecific (VstInt32 lArg, VstIntPtr lArg2, void* ptrArg, + float floatArg) SMTG_OVERRIDE; + virtual VstPlugCategory getPlugCategory () SMTG_OVERRIDE; + virtual VstInt32 canDo (char* text) SMTG_OVERRIDE; + + virtual VstInt32 getMidiProgramName (VstInt32 channel, + MidiProgramName* midiProgramName) SMTG_OVERRIDE; + virtual VstInt32 getCurrentMidiProgram (VstInt32 channel, + MidiProgramName* currentProgram) SMTG_OVERRIDE; + virtual VstInt32 getMidiProgramCategory (VstInt32 channel, + MidiProgramCategory* category) SMTG_OVERRIDE; + virtual bool hasMidiProgramsChanged (VstInt32 channel) SMTG_OVERRIDE; + virtual bool getMidiKeyName (VstInt32 channel, MidiKeyName* keyName) SMTG_OVERRIDE; + + // finally process... + virtual void processReplacing (float** inputs, float** outputs, + VstInt32 sampleFrames) SMTG_OVERRIDE; + virtual void processDoubleReplacing (double** inputs, double** outputs, + VstInt32 sampleFrames) SMTG_OVERRIDE; + virtual VstInt32 processEvents (VstEvents* events) SMTG_OVERRIDE; + + // VST 3 Interfaces ------------------------------------------------------ + // FUnknown + virtual tresult PLUGIN_API queryInterface (const char* iid, void** obj) SMTG_OVERRIDE; + virtual uint32 PLUGIN_API addRef () SMTG_OVERRIDE { return 1; } + virtual uint32 PLUGIN_API release () SMTG_OVERRIDE { return 1; } + + // IHostApplication + virtual tresult PLUGIN_API getName (String128 name) SMTG_OVERRIDE; + virtual tresult PLUGIN_API createInstance (TUID cid, TUID iid, void** obj) SMTG_OVERRIDE; + + // IComponentHandler + virtual tresult PLUGIN_API beginEdit (ParamID tag) SMTG_OVERRIDE; + virtual tresult PLUGIN_API performEdit (ParamID tag, ParamValue valueNormalized) SMTG_OVERRIDE; + virtual tresult PLUGIN_API endEdit (ParamID tag) SMTG_OVERRIDE; + virtual tresult PLUGIN_API restartComponent (int32 flags) SMTG_OVERRIDE; + + // IUnitHandler + virtual tresult PLUGIN_API notifyUnitSelection (UnitID unitId) SMTG_OVERRIDE; + virtual tresult PLUGIN_API notifyProgramListChange (ProgramListID listId, + int32 programIndex) SMTG_OVERRIDE; + + void setVendorName (char* name); + void setEffectName (char* name); + void setEffectVersion (char* version); + void setSubCategories (char* string); + + // ITimer + virtual void onTimer (Timer* timer) SMTG_OVERRIDE; + +//------------------------------------------------------------------------------------------------------- +protected: + void setupBuses (); + void setupParameters (); + void initMidiCtrlerAssignment (); + void getUnitPath (UnitID unitID, String& path); + + int32 countMainBusChannels (BusDirection dir, uint64& mainBusBitset); + + template + void setProcessingBuffers (T** inputs, T** outputs); + void setupProcessTimeInfo (); + void doProcess (VstInt32 sampleFrames); + void setEventPPQPositions (); + void processOutputEvents (); + void processMidiEvent (VstMidiEvent* midiEvent, int32 bus); + + /** Returns the last param change from guiTransfer queue. */ + bool getLastParamChange (ParamID id, ParamValue& value); + + bool setupProcessing (int32 processModeOverwrite = -1); + void addParameterChange (ParamID id, ParamValue value, int32 sampleOffset); + + bool getProgramListAndUnit (int32 midiChannel, UnitID& unitId, ProgramListID& programListId); + bool getProgramListInfoByProgramListID (ProgramListID programListId, ProgramListInfo& info); + int32 lookupProgramCategory (int32 midiChannel, String128 instrumentAttribute); + bool setupMidiProgram (int32 midiChannel, ProgramListID programListId, + MidiProgramName& midiProgramName); + + bool getPinProperties (BusDirection dir, VstInt32 pinIndex, VstPinProperties* properties); + bool pinIndexToBusChannel (BusDirection dir, VstInt32 pinIndex, int32& busIndex, + int32& busChannel); + static VstInt32 vst3ToVst2SpeakerArr (SpeakerArrangement vst3Arr); + static SpeakerArrangement vst2ToVst3SpeakerArr (VstInt32 vst2Arr); + static VstInt32 vst3ToVst2Speaker (Speaker vst3Speaker); + static void setupVst2Arrangement (VstSpeakerArrangement*& vst2arr, + Vst::SpeakerArrangement vst3Arrangement); + + struct ProgramCategory + { + MidiProgramCategory vst2Category; + String128 vst3InstrumentAttribute; + }; + std::vector> mProgramCategories; + void setupProgramCategories (); + static uint32 makeCategoriesRecursive (std::vector& channelCategories, + String128 vst3Category); + + static const int32 kMaxProgramChangeParameters = 16; + ParamID mProgramChangeParameterIDs[kMaxProgramChangeParameters]; // for each midi channel + int32 mProgramChangeParameterIdxs[kMaxProgramChangeParameters]; // for each midi channel + + VstSpeakerArrangement* mVst2InputArrangement; + VstSpeakerArrangement* mVst2OutputArrangement; + + FUID mVst3EffectClassID; + + // vst3 data + IAudioProcessor* mProcessor; + IComponent* mComponent; + IEditController* mController; + IUnitInfo* mUnitInfo; + IMidiMapping* mMidiMapping; + + bool componentInitialized; + bool controllerInitialized; + bool componentsConnected; + bool processing; + bool hasEventInputBuses; + bool hasEventOutputBuses; + + int32 mVst3SampleSize; + int32 mVst3processMode; + + char mName[PClassInfo::kNameSize]; + char mVendor[PFactoryInfo::kNameSize]; + char mSubCategories[PClassInfo2::kSubCategoriesSize]; + int32 mVersion; + + struct ParamMapEntry + { + ParamID vst3ID; + int32 vst3Index; + }; + + std::vector mParameterMap; + std::map mParamIndexMap; + ParamID mBypassParameterID; + ParamID mProgramParameterID; + int32 mProgramParameterIdx; + + HostProcessData mProcessData; + ProcessContext mProcessContext; + ParameterChanges mInputChanges; + ParameterChanges mOutputChanges; + EventList* mInputEvents; + EventList* mOutputEvents; + Vst2MidiEventQueue* mVst2OutputEvents; + uint64 mMainAudioInputBuses; + uint64 mMainAudioOutputBuses; + + ParameterChangeTransfer mInputTransfer; + ParameterChangeTransfer mOutputTransfer; + ParameterChangeTransfer mGuiTransfer; + + MemoryStream mChunk; + + Timer* mTimer; + IPluginFactory* mFactory; + + enum + { + kMaxMidiMappingBusses = 4 + }; + int32* mMidiCCMapping[kMaxMidiMappingBusses][16]; +}; + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg + +/** Must be implemented externally. */ +extern ::AudioEffect* createEffectInstance (audioMasterCallback audioMaster); + +/// \endcond diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vst2wrapper/vst2wrapper.sdk.cpp b/source/includes/vst3sdk/public.sdk/source/vst/vst2wrapper/vst2wrapper.sdk.cpp new file mode 100644 index 000000000..23c498a3f --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vst2wrapper/vst2wrapper.sdk.cpp @@ -0,0 +1,47 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vst2wrapper/vst2wrapper.sdk.cpp +// Created by : Steinberg, 01/2009 +// Description : VST 3 -> VST 2 Wrapper +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "public.sdk/source/vst/vst2wrapper/vst2wrapper.cpp" + +#include "public.sdk/source/vst/hosting/processdata.cpp" +#include "public.sdk/source/vst/hosting/eventlist.cpp" +#include "public.sdk/source/vst/hosting/hostclasses.cpp" +#include "public.sdk/source/vst/hosting/parameterchanges.cpp" + +#include "public.sdk/source/common/memorystream.cpp" + +#include "public.sdk/source/vst2.x/audioeffectx.cpp" +#include "public.sdk/source/vst2.x/audioeffect.cpp" diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vstaudioeffect.cpp b/source/includes/vst3sdk/public.sdk/source/vst/vstaudioeffect.cpp new file mode 100644 index 000000000..a2cba655e --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vstaudioeffect.cpp @@ -0,0 +1,196 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vstaudioeffect.cpp +// Created by : Steinberg, 04/2005 +// Description : Basic Audio Effect Implementation +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "vstaudioeffect.h" + +namespace Steinberg { +namespace Vst { + + +//------------------------------------------------------------------------ +// AudioEffect +//------------------------------------------------------------------------ +AudioEffect::AudioEffect () +{ + processSetup.maxSamplesPerBlock = 1024; + processSetup.processMode = Vst::kRealtime; + processSetup.sampleRate = 44100.0; + processSetup.symbolicSampleSize = Vst::kSample32; +} + +//------------------------------------------------------------------------ +AudioBus* AudioEffect::addAudioInput (const TChar* name, SpeakerArrangement arr, + BusType busType, int32 flags) +{ + AudioBus* newBus = new AudioBus (name, busType, flags, arr); + audioInputs.push_back (IPtr (newBus, false)); + return newBus; +} + +//------------------------------------------------------------------------ +AudioBus* AudioEffect::addAudioOutput (const TChar* name, SpeakerArrangement arr, + BusType busType, int32 flags) +{ + AudioBus* newBus = new AudioBus (name, busType, flags, arr); + audioOutputs.push_back (IPtr (newBus, false)); + return newBus; +} + +//------------------------------------------------------------------------ +AudioBus* AudioEffect::getAudioInput (int32 index) +{ + AudioBus* bus = FCast (audioInputs.at (index)); + return bus; +} + +//------------------------------------------------------------------------ +AudioBus* AudioEffect::getAudioOutput (int32 index) +{ + AudioBus* bus = FCast (audioOutputs.at (index)); + return bus; +} + +//------------------------------------------------------------------------ +EventBus* AudioEffect::addEventInput (const TChar* name, int32 channels, + BusType busType, int32 flags) +{ + EventBus* newBus = new EventBus (name, busType, flags, channels); + eventInputs.push_back (IPtr (newBus, false)); + return newBus; +} + +//------------------------------------------------------------------------ +EventBus* AudioEffect::addEventOutput (const TChar* name, int32 channels, + BusType busType, int32 flags) +{ + EventBus* newBus = new EventBus (name, busType, flags, channels); + eventOutputs.push_back (IPtr (newBus, false)); + return newBus; +} + +//------------------------------------------------------------------------ +EventBus* AudioEffect::getEventInput (int32 index) +{ + EventBus* bus = FCast (eventInputs.at (index)); + return bus; +} + +//------------------------------------------------------------------------ +EventBus* AudioEffect::getEventOutput (int32 index) +{ + EventBus* bus = FCast (eventOutputs.at (index)); + return bus; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API AudioEffect::setBusArrangements (SpeakerArrangement* inputs, int32 numIns, + SpeakerArrangement* outputs, int32 numOuts) +{ + if (numIns < 0 || numOuts < 0) + return kInvalidArgument; + + if (numIns > static_cast (audioInputs.size ()) || + numOuts > static_cast (audioOutputs.size ())) + return kResultFalse; + + for (int32 index = 0; index < audioInputs.size (); ++index) + { + if (index >= numIns) + break; + FCast (audioInputs[index].get ())->setArrangement (inputs[index]); + } + + for (int32 index = 0; index < audioOutputs.size (); ++index) + { + if (index >= numOuts) + break; + FCast (audioOutputs[index].get ())->setArrangement (outputs[index]); + } + + return kResultTrue; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API AudioEffect::getBusArrangement (BusDirection dir, int32 busIndex, SpeakerArrangement& arr) +{ + BusList* busList = getBusList (kAudio, dir); + if (!busList || busIndex < 0 || busList->size () <= busIndex) + return kInvalidArgument; + AudioBus* audioBus = FCast (busList->at (busIndex)); + if (audioBus) + { + arr = audioBus->getArrangement (); + return kResultTrue; + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API AudioEffect::setupProcessing (ProcessSetup& newSetup) +{ + processSetup.maxSamplesPerBlock = newSetup.maxSamplesPerBlock; + processSetup.processMode = newSetup.processMode; + processSetup.sampleRate = newSetup.sampleRate; + + if (canProcessSampleSize (newSetup.symbolicSampleSize) != kResultTrue) + return kResultFalse; + + processSetup.symbolicSampleSize = newSetup.symbolicSampleSize; + + return kResultOk; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API AudioEffect::setProcessing (TBool /*state*/) +{ + return kNotImplemented; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API AudioEffect::canProcessSampleSize (int32 symbolicSampleSize) +{ + return symbolicSampleSize == kSample32 ? kResultTrue : kResultFalse; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API AudioEffect::process (ProcessData& /*data*/) +{ + return kNotImplemented; +} + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vstaudioeffect.h b/source/includes/vst3sdk/public.sdk/source/vst/vstaudioeffect.h new file mode 100644 index 000000000..5301c8353 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vstaudioeffect.h @@ -0,0 +1,130 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vstaudioeffect.h +// Created by : Steinberg, 04/2005 +// Description : Basic Audio Effect Implementation +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "public.sdk/source/vst/vstcomponent.h" +#include "public.sdk/source/vst/vstbus.h" +#include "pluginterfaces/vst/ivstaudioprocessor.h" + +//------------------------------------------------------------------------ +namespace Steinberg { +namespace Vst { + + +//------------------------------------------------------------------------ +/** Default implementation for a VST 3 audio effect. +\ingroup vstClasses +Can be used as base class for a VST 3 effect implementation. */ +//------------------------------------------------------------------------ +class AudioEffect: public Component, + public IAudioProcessor +{ +public: +//------------------------------------------------------------------------ + /** Constructor */ + AudioEffect (); + + //---Internal Methods----------- + /** Creates and adds a new Audio input bus with a given speaker arrangement, busType (kMain or kAux). */ + AudioBus* addAudioInput (const TChar* name, SpeakerArrangement arr, + BusType busType = kMain, int32 flags = BusInfo::kDefaultActive); + + /** Creates and adds a new Audio output bus with a given speaker arrangement, busType (kMain or kAux). */ + AudioBus* addAudioOutput (const TChar* name, SpeakerArrangement arr, + BusType busType = kMain, int32 flags = BusInfo::kDefaultActive); + + /** Retrieves an Audio Input Bus by index. */ + AudioBus* getAudioInput (int32 index); + + /** Retrieves an Audio Output Bus by index. */ + AudioBus* getAudioOutput (int32 index); + + /** Creates and adds a new Event input bus with a given speaker arrangement, busType (kMain or kAux). */ + EventBus* addEventInput (const TChar* name, int32 channels = 16, + BusType busType = kMain, int32 flags = BusInfo::kDefaultActive); + + /** Creates and adds a new Event output bus with a given speaker arrangement, busType (kMain or kAux). */ + EventBus* addEventOutput (const TChar* name, int32 channels = 16, + BusType busType = kMain, int32 flags = BusInfo::kDefaultActive); + + /** Retrieves an Event Input Bus by index. */ + EventBus* getEventInput (int32 index); + + /** Retrieves an Event Output Bus by index. */ + EventBus* getEventOutput (int32 index); + + //---from IAudioProcessor------- + tresult PLUGIN_API setBusArrangements (SpeakerArrangement* inputs, int32 numIns, SpeakerArrangement* outputs, int32 numOuts) SMTG_OVERRIDE; + tresult PLUGIN_API getBusArrangement (BusDirection dir, int32 busIndex, SpeakerArrangement& arr) SMTG_OVERRIDE; + tresult PLUGIN_API canProcessSampleSize (int32 symbolicSampleSize) SMTG_OVERRIDE; + uint32 PLUGIN_API getLatencySamples () SMTG_OVERRIDE { return 0; } + tresult PLUGIN_API setupProcessing (ProcessSetup& setup) SMTG_OVERRIDE; + tresult PLUGIN_API setProcessing (TBool state) SMTG_OVERRIDE; + tresult PLUGIN_API process (ProcessData& data) SMTG_OVERRIDE; + uint32 PLUGIN_API getTailSamples () SMTG_OVERRIDE { return kNoTail; } + + //---Interface--------- + OBJ_METHODS (AudioEffect, Component) + DEFINE_INTERFACES + DEF_INTERFACE (IAudioProcessor) + END_DEFINE_INTERFACES (Component) + REFCOUNT_METHODS(Component) + + //---helpers--------- + /** Return the current channelBuffers used (depending of symbolicSampleSize). */ + void** getChannelBuffersPointer (const AudioBusBuffers& bufs) const + { + if (processSetup.symbolicSampleSize == kSample32) + return (void**)bufs.channelBuffers32; + return (void**)bufs.channelBuffers64; + } + /** Return the size in bytes of numSamples for one channel depending of symbolicSampleSize.*/ + uint32 getSampleFramesSizeInBytes (int32 numSamples) + { + if (processSetup.symbolicSampleSize == kSample32) + return numSamples * sizeof (Sample32); + return numSamples * sizeof (Sample64); + } + +//------------------------------------------------------------------------ +protected: + ProcessSetup processSetup; +}; + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vstaudioprocessoralgo.h b/source/includes/vst3sdk/public.sdk/source/vst/vstaudioprocessoralgo.h new file mode 100644 index 000000000..5a9671eaa --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vstaudioprocessoralgo.h @@ -0,0 +1,300 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vstaudioprocessoralgo.h +// Created by : Steinberg, 04/2015 +// Description : Helper algo for AudioBusBuffers +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "pluginterfaces/vst/ivstaudioprocessor.h" +#include "pluginterfaces/vst/ivstparameterchanges.h" +#include +#include + +namespace Steinberg { +namespace Vst { +namespace Algo { + +//------------------------------------------------------------------------ +template +inline void foreach (AudioBusBuffers* audioBusBuffers, int32 busCount, const T& func) +{ + if (!audioBusBuffers) + return; + + for (int32 busIndex = 0; busIndex < busCount; ++busIndex) + { + func (audioBusBuffers[busIndex]); + } +} + +//------------------------------------------------------------------------ +template +inline void foreach32 (AudioBusBuffers& audioBuffer, const T& func) +{ + for (int32 channelIndex = 0; channelIndex < audioBuffer.numChannels; ++channelIndex) + { + if (!audioBuffer.channelBuffers32[channelIndex]) + return; + + func (audioBuffer.channelBuffers32[channelIndex]); + } +} + +//------------------------------------------------------------------------ +template +inline void foreach64 (AudioBusBuffers& audioBuffer, const T& func) +{ + for (int32 channelIndex = 0; channelIndex < audioBuffer.numChannels; ++channelIndex) + { + if (!audioBuffer.channelBuffers64[channelIndex]) + return; + + func (audioBuffer.channelBuffers64[channelIndex]); + } +} + +//------------------------------------------------------------------------ +template +inline void foreach32 (AudioBusBuffers& buffer1, AudioBusBuffers& buffer2, const T& func) +{ + int32 numChannels = std::min (buffer1.numChannels, buffer2.numChannels); + + for (int32 channelIndex = 0; channelIndex < numChannels; ++channelIndex) + { + func (buffer1.channelBuffers32[channelIndex], buffer2.channelBuffers32[channelIndex], + channelIndex); + } +} + +//------------------------------------------------------------------------ +template +inline void foreach64 (AudioBusBuffers& buffer1, AudioBusBuffers& buffer2, const T& func) +{ + int32 numChannels = std::min (buffer1.numChannels, buffer2.numChannels); + + for (int32 channelIndex = 0; channelIndex < numChannels; ++channelIndex) + { + func (buffer1.channelBuffers64[channelIndex], buffer2.channelBuffers64[channelIndex], + channelIndex); + } +} + +//------------------------------------------------------------------------ +inline void copy32 (AudioBusBuffers* src, AudioBusBuffers* dest, int32 sliceSize, int32 startIndex) +{ + if (!src || !dest) + return; + + int32 numChannels = std::min (src->numChannels, dest->numChannels); + size_t numBytes = sliceSize * sizeof (Sample32); + for (int32 chIdx = 0; chIdx < numChannels; ++chIdx) + { + memcpy (&dest->channelBuffers32[chIdx][startIndex], src->channelBuffers32[chIdx], numBytes); + } +} + +//------------------------------------------------------------------------ +inline void copy64 (AudioBusBuffers* src, AudioBusBuffers* dest, int32 sliceSize, int32 startIndex) +{ + if (!src || !dest) + return; + + int32 numChannels = std::min (src->numChannels, dest->numChannels); + size_t numBytes = sliceSize * sizeof (Sample64); + for (int32 chIdx = 0; chIdx < numChannels; ++chIdx) + { + memcpy (&dest->channelBuffers64[chIdx][startIndex], src->channelBuffers64[chIdx], numBytes); + } +} + +//------------------------------------------------------------------------ +inline void clear32 (AudioBusBuffers* audioBusBuffers, int32 sampleCount, int32 busCount = 1) +{ + if (!audioBusBuffers) + return; + + const int32 numBytes = sampleCount * sizeof (Sample32); + foreach (audioBusBuffers, busCount, [&] (AudioBusBuffers& audioBuffer) { + foreach32 (audioBuffer, [&] (Sample32* channelBuffer) { + memset (channelBuffer, 0, numBytes); + }); + }); +} + +//------------------------------------------------------------------------ +inline void clear64 (AudioBusBuffers* audioBusBuffers, int32 sampleCount, int32 busCount = 1) +{ + if (!audioBusBuffers) + return; + + const int32 numBytes = sampleCount * sizeof (Sample64); + foreach (audioBusBuffers, busCount, [&](AudioBusBuffers& audioBuffer) { + foreach64 (audioBuffer, [&](Sample64* channelBuffer) { + memset (channelBuffer, 0, numBytes); + }); + }); +} + +//------------------------------------------------------------------------ +inline void mix32 (AudioBusBuffers& src, AudioBusBuffers& dest, int32 sampleCount) +{ + foreach32 (src, dest, [&] (Sample32* srcBuffer, Sample32* destBuffer, int32 /*channelIndex*/) { + for (int32 sampleIndex = 0; sampleIndex < sampleCount; ++sampleIndex) + destBuffer[sampleIndex] += srcBuffer[sampleIndex]; + }); +} + +//------------------------------------------------------------------------ +inline void mix64 (AudioBusBuffers& src, AudioBusBuffers& dest, int32 sampleCount) +{ + foreach64 (src, dest, [&] (Sample64* srcBuffer, Sample64* destBuffer, int32 /*channelIndex*/) { + for (int32 sampleIndex = 0; sampleIndex < sampleCount; ++sampleIndex) + destBuffer[sampleIndex] += srcBuffer[sampleIndex]; + }); +} + +//------------------------------------------------------------------------ +inline bool isSilent32 (AudioBusBuffers& audioBuffer, int32 sampleCount, int32 startIndex = 0) +{ + const float epsilon = 1e-10f; // under -200dB... + + sampleCount += startIndex; + for (int32 channelIndex = 0; channelIndex < audioBuffer.numChannels; ++channelIndex) + { + if (!audioBuffer.channelBuffers32[channelIndex]) + return true; + + for (int32 sampleIndex = startIndex; sampleIndex < sampleCount; ++sampleIndex) + { + float val = audioBuffer.channelBuffers32[channelIndex][sampleIndex]; + if (std::abs (val) > epsilon) + return false; + } + } + + return true; +} + +//------------------------------------------------------------------------ +inline bool isSilent64 (AudioBusBuffers& audioBuffer, int32 sampleCount, int32 startIndex = 0) +{ + const double epsilon = 1e-10f; // under -200dB... + + sampleCount += startIndex; + for (int32 channelIndex = 0; channelIndex < audioBuffer.numChannels; ++channelIndex) + { + if (!audioBuffer.channelBuffers64[channelIndex]) + return true; + + for (int32 sampleIndex = startIndex; sampleIndex < sampleCount; ++sampleIndex) + { + double val = audioBuffer.channelBuffers64[channelIndex][sampleIndex]; + if (std::abs (val) > epsilon) + return false; + } + } + + return true; +} + +//------------------------------------------------------------------------ +//------------------------------------------------------------------------ +template +inline void foreach (IEventList* eventList, const T& func) +{ + if (!eventList) + return; + + auto eventCount = eventList->getEventCount (); + for (int32 eventIndex = 0; eventIndex < eventCount; ++eventIndex) + { + Event event = {0}; + if (eventList->getEvent (eventIndex, event) != kResultOk) + continue; + + func (event); + } +} + +//------------------------------------------------------------------------ +template +inline void foreach (IParamValueQueue& paramQueue, const T& func) +{ + auto paramId = paramQueue.getParameterId (); + auto numPoints = paramQueue.getPointCount (); + for (int32 pointIndex = 0; pointIndex < numPoints; ++pointIndex) + { + int32 sampleOffset = 0; + ParamValue value = 0; + if (paramQueue.getPoint (pointIndex, sampleOffset, value) != kResultOk) + continue; + + func (paramId, sampleOffset, value); + } +} + +//------------------------------------------------------------------------ +template +inline void foreachLast (IParamValueQueue& paramQueue, const T& func) +{ + auto paramId = paramQueue.getParameterId (); + auto numPoints = paramQueue.getPointCount (); + int32 sampleOffset = 0; + ParamValue value = 0; + if (paramQueue.getPoint (numPoints - 1, sampleOffset, value) == kResultOk) + func (paramId, sampleOffset, value); +} + +//------------------------------------------------------------------------ +template +inline void foreach (IParameterChanges* changes, const T& func) +{ + if (!changes) + return; + + auto paramCount = changes->getParameterCount (); + for (int32 paramIndex = 0; paramIndex < paramCount; ++paramIndex) + { + auto paramValueQueue = changes->getParameterData (paramIndex); + if (!paramValueQueue) + continue; + + func (*paramValueQueue); + } +} + +//------------------------------------------------------------------------ +} // namespace Algo +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vstbus.cpp b/source/includes/vst3sdk/public.sdk/source/vst/vstbus.cpp new file mode 100644 index 000000000..e1d02fe0b --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vstbus.cpp @@ -0,0 +1,104 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vstbus.cpp +// Created by : Steinberg, 03/2008 +// Description : VST Bus Implementation +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "vstbus.h" + +namespace Steinberg { +namespace Vst { + +//------------------------------------------------------------------------ +// Bus Implementation +//------------------------------------------------------------------------ +Bus::Bus (const TChar* _name, BusType _busType, int32 _flags) +: name (_name) +, busType (_busType) +, flags (_flags) +, active (false) +{} + +//------------------------------------------------------------------------ +bool Bus::getInfo (BusInfo& info) +{ + name.copyTo16 (info.name, 0, str16BufferSize (info.name) - 1); + info.busType = busType; + info.flags = flags; + return true; +} + + +//------------------------------------------------------------------------ +// EventBus Implementation +//------------------------------------------------------------------------ +EventBus::EventBus (const TChar* name, BusType busType, int32 flags, int32 channelCount) +: Bus (name, busType, flags) +, channelCount (channelCount) +{} + +//------------------------------------------------------------------------ +bool EventBus::getInfo (BusInfo& info) +{ + info.channelCount = channelCount; + return Bus::getInfo (info); +} + + +//------------------------------------------------------------------------ +// AudioBus Implementation +//------------------------------------------------------------------------ +AudioBus::AudioBus (const TChar* name, BusType busType, int32 flags, SpeakerArrangement arr) +: Bus (name, busType, flags) +, speakerArr (arr) +{} + +//------------------------------------------------------------------------ +bool AudioBus::getInfo (BusInfo& info) +{ + info.channelCount = SpeakerArr::getChannelCount (speakerArr); + return Bus::getInfo (info); +} + + +//------------------------------------------------------------------------ +// BusList Implementation +//------------------------------------------------------------------------ +BusList::BusList (MediaType type, BusDirection dir) +: type (type) +, direction (dir) +{} + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vstbus.h b/source/includes/vst3sdk/public.sdk/source/vst/vstbus.h new file mode 100644 index 000000000..990d4aa84 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vstbus.h @@ -0,0 +1,164 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vstbus.h +// Created by : Steinberg, 03/2008 +// Description : VST Bus Implementation +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "base/source/fobject.h" +#include "base/source/fstring.h" +#include "pluginterfaces/vst/ivstcomponent.h" +#include "pluginterfaces/vst/ivstaudioprocessor.h" + +#include + +//------------------------------------------------------------------------ +namespace Steinberg { +namespace Vst { + +//------------------------------------------------------------------------ +/** Basic Bus object. +\ingroup vstClasses */ +//------------------------------------------------------------------------ +class Bus: public FObject +{ +public: +//------------------------------------------------------------------------ + /** Constructor. */ + Bus (const TChar* name, BusType busType, int32 flags); + + /** Returns true if the bus is active. */ + TBool isActive () const { return active; } + + /** Activates the bus. */ + void setActive (TBool state) { active = state; } + + /** Sets a new name for this bus. */ + void setName (String newName) { name = newName; } + + /** Sets a new busType for this bus. */ + void setBusType (BusType newBusType) { busType = newBusType; } + + /** Sets a new flags for this bus. */ + void setFlags (uint32 newFlags) { flags = newFlags; } + + /** Gets the BusInfo of this bus. */ + virtual bool getInfo (BusInfo&); + + OBJ_METHODS (Vst::Bus, FObject) +//------------------------------------------------------------------------ +protected: + String name; ///< name + BusType busType; ///< kMain or kAux, see \ref BusTypes + int32 flags; ///< flags, see \ref BusFlags + TBool active; ///< activation state +}; + +//------------------------------------------------------------------------ +/** Description of an Event Bus. +\ingroup vstClasses */ +//------------------------------------------------------------------------ +class EventBus: public Bus +{ +public: +//------------------------------------------------------------------------ + /** Constructor of an EventBus. */ + EventBus (const TChar* name, BusType busType, int32 flags, int32 channelCount); + + //---from Bus------- + /** Gets the BusInfo associated to this Event bus. */ + virtual bool getInfo (BusInfo& info) SMTG_OVERRIDE; + + OBJ_METHODS (Vst::EventBus, Vst::Bus); + +//------------------------------------------------------------------------ +protected: + int32 channelCount; +}; + +//------------------------------------------------------------------------ +/** Description of an Audio Bus. +\ingroup vstClasses */ +//------------------------------------------------------------------------ +class AudioBus: public Bus +{ +public: +//------------------------------------------------------------------------ + AudioBus (const TChar* name, BusType busType, int32 flags, SpeakerArrangement arr); + + /** Gets the speaker arrangement defining this Audio bus. */ + SpeakerArrangement getArrangement () const { return speakerArr; } + + /** Sets the speaker arrangement defining this Audio bus. */ + void setArrangement (const SpeakerArrangement& arr) { speakerArr = arr; } + + //---from Bus--------------------- + /** Gets the BusInfo associated to this Audio bus. */ + virtual bool getInfo (BusInfo& info) SMTG_OVERRIDE; + + OBJ_METHODS (Vst::AudioBus, Vst::Bus) + +//------------------------------------------------------------------------ +protected: + SpeakerArrangement speakerArr; +}; + +//------------------------------------------------------------------------ +/** List of Buses. +\ingroup vstClasses */ +//------------------------------------------------------------------------ +class BusList: public FObject, public std::vector > +{ +public: +//------------------------------------------------------------------------ + /** Constructor. */ + BusList (MediaType type, BusDirection dir); + + /** Returns the BusList Type. See \ref MediaType */ + MediaType getType () const { return type; } + + /** Returns the BusList direction. See \ref BusDirection */ + BusDirection getDirection () const { return direction; } + + OBJ_METHODS (Vst::BusList, FObject); + +//------------------------------------------------------------------------ +protected: + MediaType type; + BusDirection direction; +}; + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vstbypassprocessor.cpp b/source/includes/vst3sdk/public.sdk/source/vst/vstbypassprocessor.cpp new file mode 100644 index 000000000..87c870ba9 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vstbypassprocessor.cpp @@ -0,0 +1,353 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vstbypassprocessor.cpp +// Created by : Steinberg, 04/2015 +// Description : Example of bypass support Implementation +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "vstbypassprocessor.h" +#include "vstspeakerarray.h" + +#include + +namespace Steinberg { +namespace Vst { + +//------------------------------------------------------------------------ +static bool delay (int32 sampleFrames, float* inStream, float* outStream, float* delayBuffer, + int32 bufferSize, int32 bufferInPos, int32 bufferOutPos) +{ + // delay inStream + int32 remain, inFrames, outFrames; + float* bufIn; + float* bufOut; + + remain = sampleFrames; + while (remain > 0) + { + bufIn = delayBuffer + bufferInPos; + bufOut = delayBuffer + bufferOutPos; + + if (bufferInPos > bufferOutPos) + inFrames = bufferSize - bufferInPos; + else + inFrames = bufferOutPos - bufferInPos; + + outFrames = bufferSize - bufferOutPos; + + if (inFrames > remain) + inFrames = remain; + + if (outFrames > inFrames) + outFrames = inFrames; + + // order important for in-place processing! + memcpy (bufIn, inStream, inFrames * sizeof (float)); // copy to buffer + memcpy (outStream, bufOut, outFrames * sizeof (float)); // copy from buffer + + inStream += inFrames; + outStream += outFrames; + + bufferInPos += inFrames; + if (bufferInPos >= bufferSize) + bufferInPos -= bufferSize; + bufferOutPos += outFrames; + if (bufferOutPos >= bufferSize) + bufferOutPos -= bufferSize; + + if (inFrames > outFrames) + { + // still some output to copy + bufOut = delayBuffer + bufferOutPos; + outFrames = inFrames - outFrames; + + memcpy (outStream, bufOut, outFrames * sizeof (float)); // copy from buffer + + outStream += outFrames; + + bufferOutPos += outFrames; + if (bufferOutPos >= bufferSize) + bufferOutPos -= bufferSize; + } + + remain -= inFrames; + } + + return true; +} + + +//------------------------------------------------------------------------ +// AudioBuffer Implementation +//------------------------------------------------------------------------ +AudioBuffer::AudioBuffer () +: mBuffer (nullptr) +, mMaxSamples (0) +{} + +//------------------------------------------------------------------------ +AudioBuffer::~AudioBuffer () +{ + resize (0); +} + +//------------------------------------------------------------------------ +void AudioBuffer::resize (int32 _maxSamples) +{ + if (mMaxSamples != _maxSamples) + { + mMaxSamples = _maxSamples; + if (mMaxSamples <= 0) + { + if (mBuffer) + free (mBuffer), + mBuffer = nullptr; + } + else + { + if (mBuffer) + mBuffer = (float*)realloc (mBuffer, mMaxSamples * sizeof (float)); + else + mBuffer = (float*)malloc (mMaxSamples * sizeof (float)); + } + } +} + +//------------------------------------------------------------------------ +void AudioBuffer::clear (int32 numSamples) +{ + if (mBuffer) + { + int32 count = numSamples < mMaxSamples ? numSamples : mMaxSamples; + memset (mBuffer, 0, count * sizeof (float)); + } +} + +//------------------------------------------------------------------------ +//------------------------------------------------------------------------ +// BypassProcessor Implementation +//------------------------------------------------------------------------ +BypassProcessor::BypassProcessor () : mActive (false), mMainIOBypass (false) +{ + for (int32 i = 0; i < kMaxChannelsSupported; i++) + { + mInputPinLookup[i] = -1; + mDelays[i] = nullptr; + } +} + +//------------------------------------------------------------------------ +BypassProcessor::~BypassProcessor () { reset (); } + +//------------------------------------------------------------------------ +void BypassProcessor::setup (IAudioProcessor& audioProcessor, ProcessSetup& processSetup, + int32 delaySamples) +{ + reset (); + + SpeakerArrangement inputArr = 0; + bool hasInput = audioProcessor.getBusArrangement (kInput, 0, inputArr) == kResultOk; + + SpeakerArrangement outputArr = 0; + bool hasOutput = audioProcessor.getBusArrangement (kOutput, 0, outputArr) == kResultOk; + + mMainIOBypass = hasInput && hasOutput; + if (!mMainIOBypass) + return; + + // create lookup table (in <- out) and delays... + SpeakerArray inArray (inputArr); + SpeakerArray outArray (outputArr); + + // security check (todo) + if (outArray.total () >= kMaxChannelsSupported) + return; + + for (int32 i = 0; i < outArray.total (); i++) + { + if (outArray.at (i) == Vst::kSpeakerL) + { + if (inArray.total () == 1 && inArray.at (0) == Vst::kSpeakerM) + { + mInputPinLookup[i] = 0; + } + else + mInputPinLookup[i] = inArray.getSpeakerIndex (outArray.at (i)); + } + else + mInputPinLookup[i] = inArray.getSpeakerIndex (outArray.at (i)); + + mDelays[i] = new Delay (processSetup.maxSamplesPerBlock, delaySamples); + mDelays[i]->flush (); + } +} + +//------------------------------------------------------------------------ +void BypassProcessor::reset () +{ + mMainIOBypass = false; + + for (int32 i = 0; i < kMaxChannelsSupported; i++) + { + mInputPinLookup[i] = -1; + if (mDelays[i]) + { + delete mDelays[i]; + mDelays[i] = nullptr; + } + } +} + +//------------------------------------------------------------------------ +void BypassProcessor::setActive (bool state) +{ + if (mActive == state) + return; + + mActive = state; + + // flush delays when turning on + if (state && mMainIOBypass) + for (int32 i = 0; i < kMaxChannelsSupported; i++) + { + if (!mDelays[i]) + break; + mDelays[i]->flush (); + } +} + +//------------------------------------------------------------------------ +void BypassProcessor::process (ProcessData& data) +{ + if (mMainIOBypass) + { + AudioBusBuffers& inBus = data.inputs[0]; + AudioBusBuffers& outBus = data.outputs[0]; + + for (int32 channel = 0; channel < outBus.numChannels; channel++) + { + float* src = nullptr; + bool silent = true; + float* dst = outBus.channelBuffers32[channel]; + if (!dst) + continue; + + int inputChannel = mInputPinLookup[channel]; + if (inputChannel != -1) + { + silent = (inBus.silenceFlags & (1ll << inputChannel)) != 0; + src = inBus.channelBuffers32[inputChannel]; + } + + if (mDelays[channel]->process (src, dst, data.numSamples, silent)) + { + outBus.silenceFlags |= (1ll << channel); + } + else + { + outBus.silenceFlags = 0; + } + } + } + + // clear all other outputs + for (int32 outBusIndex = mMainIOBypass ? 1 : 0; outBusIndex < data.numOutputs; outBusIndex++) + { + AudioBusBuffers& outBus = data.outputs[outBusIndex]; + if (!outBus.channelBuffers32) + continue; + + for (int32 channel = 0; channel < outBus.numChannels; channel++) + { + float* dst = outBus.channelBuffers32[channel]; + if (dst) + { + memset (dst, 0, data.numSamples * sizeof (float)); + outBus.silenceFlags |= 1ll << channel; + } + } + } +} + +//------------------------------------------------------------------------ +void BypassProcessor::Delay::flush () +{ + mDelayBuffer.clearAll (); + + mInPos = mOutPos = 0; + if (hasDelay ()) + mOutPos = getBufferSamples () - mDelaySamples; // must be != inPos +} + +//------------------------------------------------------------------------ +bool BypassProcessor::Delay::process (float* src, float* dst, int32 numSamples, bool silentIn) +{ + bool silentOut = false; + + if (hasDelay () && src) + { + int32 bufferSize = getBufferSamples (); + delay (numSamples, src, dst, mDelayBuffer, bufferSize, mInPos, mOutPos); + + // update inPos, outPos + mInPos += numSamples; + if (mInPos >= bufferSize) + mInPos -= bufferSize; + mOutPos += numSamples; + if (mOutPos >= bufferSize) + mOutPos -= bufferSize; + } + else + { + if (src != dst) + { + if (src && !silentIn) + { + memcpy (dst, src, numSamples * sizeof (float)); + } + else + { + memset (dst, 0, numSamples * sizeof (float)); + silentOut = true; + } + } + else + { + silentOut = silentIn; + } + } + return silentOut; +} + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vstbypassprocessor.h b/source/includes/vst3sdk/public.sdk/source/vst/vstbypassprocessor.h new file mode 100644 index 000000000..cea313b17 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vstbypassprocessor.h @@ -0,0 +1,118 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vstbypassprocessor.h +// Created by : Steinberg, 04/2015 +// Description : Example of bypass support Implementation +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "pluginterfaces/vst/ivstaudioprocessor.h" + +namespace Steinberg { +namespace Vst { + +#define kMaxChannelsSupported 64 + +//------------------------------------------------------------------------ +// AudioBuffer +//------------------------------------------------------------------------ +class AudioBuffer +{ +public: +//------------------------------------------------------------------------ + AudioBuffer (); + ~AudioBuffer (); + + int32 getMaxSamples () const { return mMaxSamples; } + void resize (int32 _maxSamples); + void release () { resize (0); } + void clear (int32 numSamples); + void clearAll () { if (mMaxSamples > 0) clear (mMaxSamples); } + + operator float* () { return mBuffer; } +//------------------------------------------------------------------------ +protected: + float* mBuffer; + int32 mMaxSamples; +}; + +//------------------------------------------------------------------------ +// BypassProcessor +//------------------------------------------------------------------------ +class BypassProcessor +{ +public: +//------------------------------------------------------------------------ + BypassProcessor (); + ~BypassProcessor (); + + void setup (IAudioProcessor& audioProcessor, ProcessSetup& processSetup, int32 delaySamples); + void reset (); + + bool isActive () const { return mActive; } + void setActive (bool state); + + void process (ProcessData& data); +//------------------------------------------------------------------------ +protected: + bool mActive; + bool mMainIOBypass; + int32 mInputPinLookup[kMaxChannelsSupported]; + + struct Delay + { + AudioBuffer mDelayBuffer; + int32 mDelaySamples; + int32 mInPos; + int32 mOutPos; + + Delay (int32 maxSamplesPerBlock, int32 delaySamples) + : mDelaySamples (delaySamples), mInPos (-1), mOutPos (-1) + { + if (mDelaySamples > 0) + mDelayBuffer.resize (maxSamplesPerBlock + mDelaySamples); + } + + bool hasDelay () const { return mDelaySamples > 0; } + int32 getBufferSamples () const { return mDelayBuffer.getMaxSamples (); } + + bool process (float* src, float* dst, int32 numSamples, bool silentIn); + void flush (); + }; + + Delay* mDelays[kMaxChannelsSupported]; +}; + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vstcomponent.cpp b/source/includes/vst3sdk/public.sdk/source/vst/vstcomponent.cpp new file mode 100644 index 000000000..b69241908 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vstcomponent.cpp @@ -0,0 +1,214 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vstcomponent.cpp +// Created by : Steinberg, 04/2005 +// Description : Basic VST Plug-in Implementation +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "vstcomponent.h" + +namespace Steinberg { +namespace Vst { + + +//------------------------------------------------------------------------ +// Component Implementation +//------------------------------------------------------------------------ +Component::Component () +: audioInputs (kAudio, kInput) +, audioOutputs (kAudio, kOutput) +, eventInputs (kEvent, kInput) +, eventOutputs (kEvent, kOutput) +{} + +//------------------------------------------------------------------------ +tresult PLUGIN_API Component::initialize (FUnknown* context) +{ + return ComponentBase::initialize (context); +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API Component::terminate () +{ + // remove all buses + removeAllBusses (); + + return ComponentBase::terminate (); +} + +//------------------------------------------------------------------------ +BusList* Component::getBusList (MediaType type, BusDirection dir) +{ + if (type == kAudio) + return dir == kInput ? &audioInputs : &audioOutputs; + else if (type == kEvent) + return dir == kInput ? &eventInputs : &eventOutputs; + return 0; +} + +//------------------------------------------------------------------------ +tresult Component::removeAudioBusses () +{ + audioInputs.clear (); + audioOutputs.clear (); + + return kResultOk; +} + +//------------------------------------------------------------------------ +tresult Component::removeEventBusses () +{ + eventInputs.clear (); + eventOutputs.clear (); + + return kResultOk; +} + +//------------------------------------------------------------------------ +tresult Component::removeAllBusses () +{ + removeAudioBusses (); + removeEventBusses (); + + return kResultOk; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API Component::getControllerClassId (TUID classID) +{ + if (controllerClass.isValid ()) + { + controllerClass.toTUID (classID); + return kResultTrue; + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API Component::setIoMode (IoMode /*mode*/) +{ + return kNotImplemented; +} + +//------------------------------------------------------------------------ +int32 PLUGIN_API Component::getBusCount (MediaType type, BusDirection dir) +{ + BusList* busList = getBusList (type, dir); + return busList ? static_cast (busList->size ()) : 0; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API Component::getBusInfo (MediaType type, BusDirection dir, int32 index, BusInfo& info) +{ + if (index < 0) + return kInvalidArgument; + BusList* busList = getBusList (type, dir); + if (busList == 0) + return kInvalidArgument; + if (index >= static_cast (busList->size ())) + return kInvalidArgument; + + Bus* bus = busList->at (index); + info.mediaType = type; + info.direction = dir; + if (bus->getInfo (info)) + return kResultTrue; + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API Component::getRoutingInfo (RoutingInfo& /*inInfo*/, RoutingInfo& /*outInfo*/) +{ + return kNotImplemented; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API Component::activateBus (MediaType type, BusDirection dir, int32 index, TBool state) +{ + if (index < 0) + return kInvalidArgument; + BusList* busList = getBusList (type, dir); + if (busList == 0) + return kInvalidArgument; + if (index >= static_cast (busList->size ())) + return kInvalidArgument; + + Bus* bus = busList->at (index); + bus->setActive (state); + return kResultTrue; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API Component::setActive (TBool /*state*/) +{ + return kResultOk; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API Component::setState (IBStream* /*state*/) +{ + return kNotImplemented; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API Component::getState (IBStream* /*state*/) +{ + return kNotImplemented; +} + +//------------------------------------------------------------------------ +tresult Component::renameBus (MediaType type, BusDirection dir, int32 index, const String128 newName) +{ + if (index < 0) + return kInvalidArgument; + BusList* busList = getBusList (type, dir); + if (busList == 0) + return kInvalidArgument; + if (index >= static_cast (busList->size ())) + return kInvalidArgument; + + Bus* bus = busList->at (index); + bus->setName (newName); + return kResultTrue; +} + + +//------------------------------------------------------------------------ +// Helpers Implementation +//------------------------------------------------------------------------ +tresult getSpeakerChannelIndex (SpeakerArrangement arrangement, uint64 speaker, int32& channel) +{ + channel = SpeakerArr::getSpeakerIndex (speaker, arrangement); + return channel < 0 ? kResultFalse : kResultTrue; +} +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vstcomponent.h b/source/includes/vst3sdk/public.sdk/source/vst/vstcomponent.h new file mode 100644 index 000000000..100e8d86c --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vstcomponent.h @@ -0,0 +1,118 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vstcomponent.h +// Created by : Steinberg, 04/2005 +// Description : Basic VST Plug-in Implementation +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "public.sdk/source/vst/vstcomponentbase.h" +#include "public.sdk/source/vst/vstbus.h" +#include "pluginterfaces/vst/ivstcomponent.h" + +//------------------------------------------------------------------------ +namespace Steinberg { +namespace Vst { + + +//------------------------------------------------------------------------ +/** Default implementation for a VST 3 Component. +\ingroup vstClasses +Can be used as base class for a VST 3 component implementation. */ +//------------------------------------------------------------------------ +class Component: public ComponentBase, + public IComponent +{ +public: +//------------------------------------------------------------------------ + /** Constructor */ + Component (); + + //---Internal Methods------- + /** Sets the controller Class ID associated to its component. */ + void setControllerClass (const FUID& cid) { controllerClass = cid; } + + /** Removes all Audio Buses. */ + tresult removeAudioBusses (); + + /** Removes all Event Buses. */ + tresult removeEventBusses (); + + /** Renames a specific bus. Do not forget to inform the host about this (see \ref IComponentHandler::restartComponent (kIoTitlesChanged)). */ + tresult renameBus (MediaType type, BusDirection dir, int32 index, const String128 newName); + + //---from IComponent-------- + tresult PLUGIN_API getControllerClassId (TUID classID) SMTG_OVERRIDE; + tresult PLUGIN_API setIoMode (IoMode mode) SMTG_OVERRIDE; + int32 PLUGIN_API getBusCount (MediaType type, BusDirection dir) SMTG_OVERRIDE; + tresult PLUGIN_API getBusInfo (MediaType type, BusDirection dir, int32 index, BusInfo& info) SMTG_OVERRIDE; + tresult PLUGIN_API getRoutingInfo (RoutingInfo& inInfo, RoutingInfo& outInfo) SMTG_OVERRIDE; + tresult PLUGIN_API activateBus (MediaType type, BusDirection dir, int32 index, TBool state) SMTG_OVERRIDE; + tresult PLUGIN_API setActive (TBool state) SMTG_OVERRIDE; + tresult PLUGIN_API setState (IBStream* state) SMTG_OVERRIDE; + tresult PLUGIN_API getState (IBStream* state) SMTG_OVERRIDE; + + //---from ComponentBase------ + tresult PLUGIN_API initialize (FUnknown* context) SMTG_OVERRIDE; + tresult PLUGIN_API terminate () SMTG_OVERRIDE; + + //---Interface--------- + OBJ_METHODS (Component, ComponentBase) + DEFINE_INTERFACES + DEF_INTERFACE (IComponent) + END_DEFINE_INTERFACES (ComponentBase) + REFCOUNT_METHODS(ComponentBase) + +//------------------------------------------------------------------------ +protected: + FUID controllerClass; + BusList audioInputs; + BusList audioOutputs; + BusList eventInputs; + BusList eventOutputs; + + BusList* getBusList (MediaType type, BusDirection dir); + tresult removeAllBusses (); +}; + + +//------------------------------------------------------------------------ +// some Helper functions.... +//------------------------------------------------------------------------ + +/** Gets the channel index of a given speaker in a arrangement, returns kResultFalse if speaker not part of the arrangement else returns kResultTrue. */ +tresult getSpeakerChannelIndex (SpeakerArrangement arrangement, uint64 speaker, int32& channel); + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vstcomponentbase.cpp b/source/includes/vst3sdk/public.sdk/source/vst/vstcomponentbase.cpp new file mode 100644 index 000000000..e65d208ba --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vstcomponentbase.cpp @@ -0,0 +1,179 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vstcomponentbase.cpp +// Created by : Steinberg, 05/2005 +// Description : Base class for VST Component and Edit Controller +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "vstcomponentbase.h" +#include "base/source/fstring.h" + +namespace Steinberg { +namespace Vst { + +//------------------------------------------------------------------------ +// ComponentBase Implementation +//------------------------------------------------------------------------ +ComponentBase::ComponentBase () +: hostContext (0) +, peerConnection (0) +{} + +//------------------------------------------------------------------------ +ComponentBase::~ComponentBase () +{} + +//------------------------------------------------------------------------ +tresult PLUGIN_API ComponentBase::initialize (FUnknown* context) +{ + // check if already initialized + if (hostContext) + return kResultFalse; + + hostContext = context; + if (hostContext) + hostContext->addRef (); + + return kResultOk; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API ComponentBase::terminate () +{ + // release host interfaces + if (hostContext) + { + hostContext->release (); + hostContext = 0; + } + + // in case host did not disconnect us, + // release peer now + if (peerConnection) + { + peerConnection->disconnect (this); + peerConnection->release (); + peerConnection = 0; + } + + return kResultOk; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API ComponentBase::connect (IConnectionPoint* other) +{ + if (!other) + return kInvalidArgument; + + // check if already connected + if (peerConnection) + return kResultFalse; + + peerConnection = other; + peerConnection->addRef (); + return kResultOk; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API ComponentBase::disconnect (IConnectionPoint* other) +{ + if (peerConnection && other == peerConnection) + { + peerConnection->release (), + peerConnection = 0; + return kResultOk; + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API ComponentBase::notify (IMessage* message) +{ + if (!message) + return kInvalidArgument; + + if (!strcmp (message->getMessageID (), "TextMessage")) + { + TChar string[256] = {0}; + if (message->getAttributes ()->getString ("Text", string, sizeof (string) / sizeof (char16)) == kResultOk) + { + String tmp (string); + tmp.toMultiByte (kCP_Utf8); + return receiveText (tmp.text8 ()); + } + } + + return kResultFalse; +} + +//------------------------------------------------------------------------ +IMessage* ComponentBase::allocateMessage () +{ + FUnknownPtr hostApp (hostContext); + if (hostApp) + return Vst::allocateMessage (hostApp); + return 0; +} + +//------------------------------------------------------------------------ +tresult ComponentBase::sendMessage (IMessage* message) +{ + if (message != 0 && getPeer () != 0) + return getPeer ()->notify (message); + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult ComponentBase::sendTextMessage (const char8* text) +{ + IMessage* message = allocateMessage (); + if (!message) + return kResultFalse; + + FReleaser msgReleaser (message); + message->setMessageID ("TextMessage"); + String tmp (text, kCP_Utf8); + if (tmp.length () >= 256) + tmp.remove (255); + message->getAttributes ()->setString ("Text", tmp.text16 ()); + return sendMessage (message); +} + +//------------------------------------------------------------------------ +tresult ComponentBase::receiveText (const char8* /*text*/) +{ + return kResultOk; +} + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vstcomponentbase.h b/source/includes/vst3sdk/public.sdk/source/vst/vstcomponentbase.h new file mode 100644 index 000000000..50a8f243e --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vstcomponentbase.h @@ -0,0 +1,105 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vstcomponentbase.h +// Created by : Steinberg, 05/2005 +// Description : Base class for Component and Edit Controller +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "pluginterfaces/base/ipluginbase.h" +#include "pluginterfaces/vst/ivstmessage.h" +#include "pluginterfaces/vst/ivsthostapplication.h" +#include "base/source/fobject.h" + +//------------------------------------------------------------------------ +namespace Steinberg { +namespace Vst { + +//------------------------------------------------------------------------ +/** Base class for VST 3 Component and Edit Controller. +\ingroup vstClasses */ +//------------------------------------------------------------------------ +class ComponentBase: public FObject, + public IPluginBase, + public IConnectionPoint +{ +public: +//------------------------------------------------------------------------ + ComponentBase (); + virtual ~ComponentBase (); + + //--- Internal Methods------ + /** Returns the hostContext (set by the host during initialize call). */ + FUnknown* getHostContext () { return hostContext; } + + /** Returns the peer for the messaging communication (you can only use IConnectionPoint::notify for communicate between peers, do not try to cast peerConnection. */ + IConnectionPoint* getPeer () { return peerConnection; } + + /** Allocates a message instance (don't forget to release it). */ + IMessage* allocateMessage (); + + /** Sends the given message to the peer. */ + tresult sendMessage (IMessage* message); + + /** Sends a simple text message to the peer (max 255 characters). Text is interpreted as UTF-8. */ + tresult sendTextMessage (const char8* text); + + /** Receives a simple text message from the peer (max 255 characters). Text is UTF-8 encoded. */ + virtual tresult receiveText (const char8* text); + + //---from IPluginBase------ + virtual tresult PLUGIN_API initialize (FUnknown* context) SMTG_OVERRIDE; + virtual tresult PLUGIN_API terminate () SMTG_OVERRIDE; + + //---from IConnectionPoint----------- + tresult PLUGIN_API connect (IConnectionPoint* other) SMTG_OVERRIDE; + tresult PLUGIN_API disconnect (IConnectionPoint* other) SMTG_OVERRIDE; + tresult PLUGIN_API notify (IMessage* message) SMTG_OVERRIDE; + + //---Interface------ + OBJ_METHODS (ComponentBase, FObject) + DEFINE_INTERFACES + DEF_INTERFACE (IPluginBase) + DEF_INTERFACE (IConnectionPoint) + END_DEFINE_INTERFACES (FObject) + REFCOUNT_METHODS(FObject) + +//------------------------------------------------------------------------ +protected: + FUnknown* hostContext; + IConnectionPoint* peerConnection; +}; + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vsteditcontroller.cpp b/source/includes/vst3sdk/public.sdk/source/vst/vsteditcontroller.cpp new file mode 100644 index 000000000..642f2ca5e --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vsteditcontroller.cpp @@ -0,0 +1,718 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vsteditcontroller.cpp +// Created by : Steinberg, 04/2005 +// Description : VST Edit Controller Implementation +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "public.sdk/source/vst/vsteditcontroller.h" +#include "pluginterfaces/base/ustring.h" +#include "base/source/updatehandler.h" + +#include + +namespace Steinberg { +namespace Vst { + +KnobMode EditController::hostKnobMode = kCircularMode; + +//------------------------------------------------------------------------ +// EditController Implementation +//------------------------------------------------------------------------ +EditController::EditController () +: componentHandler (0) +, componentHandler2 (0) +{} + +//------------------------------------------------------------------------ +tresult PLUGIN_API EditController::initialize (FUnknown* context) +{ + return ComponentBase::initialize (context); +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API EditController::terminate () +{ + parameters.removeAll (); + + if (componentHandler) + { + componentHandler->release (); + componentHandler = 0; + } + + if (componentHandler2) + { + componentHandler2->release (); + componentHandler2 = 0; + } + + return ComponentBase::terminate (); +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API EditController::setComponentState (IBStream* /*state*/) +{ + return kNotImplemented; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API EditController::setState (IBStream* /*state*/) +{ + return kNotImplemented; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API EditController::getState (IBStream* /*state*/) +{ + return kNotImplemented; +} + +//------------------------------------------------------------------------ +int32 PLUGIN_API EditController::getParameterCount () +{ + return parameters.getParameterCount (); +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API EditController::getParameterInfo (int32 paramIndex, ParameterInfo& info) +{ + Parameter* parameter = parameters.getParameterByIndex (paramIndex); + if (parameter) + { + info = parameter->getInfo (); + return kResultTrue; + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API EditController::getParamStringByValue (ParamID tag, ParamValue valueNormalized, String128 string) +{ + Parameter* parameter = getParameterObject (tag); + if (parameter) + { + parameter->toString (valueNormalized, string); + return kResultTrue; + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API EditController::getParamValueByString (ParamID tag, TChar* string, ParamValue& valueNormalized) +{ + Parameter* parameter = getParameterObject (tag); + if (parameter) + { + if (parameter->fromString (string, valueNormalized)) + { + return kResultTrue; + } + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +ParamValue PLUGIN_API EditController::normalizedParamToPlain (ParamID tag, ParamValue valueNormalized) +{ + Parameter* parameter = getParameterObject (tag); + if (parameter) + { + return parameter->toPlain (valueNormalized); + } + return valueNormalized; +} + +//------------------------------------------------------------------------ +ParamValue PLUGIN_API EditController::plainParamToNormalized (ParamID tag, ParamValue plainValue) +{ + Parameter* parameter = getParameterObject (tag); + if (parameter) + { + return parameter->toNormalized (plainValue); + } + return plainValue; +} + +//------------------------------------------------------------------------ +ParamValue PLUGIN_API EditController::getParamNormalized (ParamID tag) +{ + Parameter* parameter = getParameterObject (tag); + if (parameter) + { + return parameter->getNormalized (); + } + return 0.0; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API EditController::setParamNormalized (ParamID tag, ParamValue value) +{ + Parameter* parameter = getParameterObject (tag); + if (parameter) + { + parameter->setNormalized (value); + return kResultTrue; + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API EditController::setComponentHandler (IComponentHandler* newHandler) +{ + if (componentHandler == newHandler) + { + return kResultTrue; + } + + if (componentHandler) + { + componentHandler->release (); + } + + componentHandler = newHandler; + if (componentHandler) + { + componentHandler->addRef (); + } + + // try to get the extended version + if (componentHandler2) + { + componentHandler2->release (); + componentHandler2 = 0; + } + + if (newHandler) + { + newHandler->queryInterface (IComponentHandler2::iid, (void**)&componentHandler2); + } + return kResultTrue; +} + +//------------------------------------------------------------------------ +tresult EditController::beginEdit (ParamID tag) +{ + if (componentHandler) + { + return componentHandler->beginEdit (tag); + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult EditController::performEdit (ParamID tag, ParamValue valueNormalized) +{ + if (componentHandler) + { + return componentHandler->performEdit (tag, valueNormalized); + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult EditController::endEdit (ParamID tag) +{ + if (componentHandler) + { + return componentHandler->endEdit (tag); + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult EditController::startGroupEdit () +{ + if (componentHandler2) + { + return componentHandler2->startGroupEdit (); + } + return kNotImplemented; +} + +//------------------------------------------------------------------------ +tresult EditController::finishGroupEdit () +{ + if (componentHandler2) + { + return componentHandler2->finishGroupEdit (); + } + return kNotImplemented; +} + +//------------------------------------------------------------------------ +tresult EditController::getParameterInfoByTag (ParamID tag, ParameterInfo& info) +{ + Parameter* parameter = getParameterObject (tag); + if (parameter) + { + info = parameter->getInfo (); + return kResultTrue; + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult EditController::setDirty (TBool state) +{ + if (componentHandler2) + { + return componentHandler2->setDirty (state); + } + return kNotImplemented; +} + +//------------------------------------------------------------------------ +tresult EditController::requestOpenEditor (FIDString name) +{ + if (componentHandler2) + { + return componentHandler2->requestOpenEditor (name); + } + return kNotImplemented; +} + +//------------------------------------------------------------------------ +// EditorView Implementation +//------------------------------------------------------------------------ +EditorView::EditorView (EditController* controller, ViewRect* size) +: CPluginView (size) +, controller (controller) +{ + if (controller) + { + controller->addRef (); + } +} + +//------------------------------------------------------------------------ +EditorView::~EditorView () +{ + if (controller) + { + controller->editorDestroyed (this); + controller->release (); + } +} + +//------------------------------------------------------------------------ +void EditorView::attachedToParent () +{ + if (controller) + { + controller->editorAttached (this); + } +} + +//------------------------------------------------------------------------ +void EditorView::removedFromParent () +{ + if (controller) + { + controller->editorRemoved (this); + } +} + +//------------------------------------------------------------------------ +// EditControllerEx1 implementation +//------------------------------------------------------------------------ +EditControllerEx1::EditControllerEx1 () : selectedUnit (kRootUnitId) { UpdateHandler::instance (); } + +//------------------------------------------------------------------------ +EditControllerEx1::~EditControllerEx1 () +{ + for (ProgramListVector::const_iterator it = programLists.begin (), end = programLists.end (); + it != end; ++it) + { + if (*it) + (*it)->removeDependent (this); + } +} + +//------------------------------------------------------------------------ +bool EditControllerEx1::addUnit (Unit* unit) +{ + units.push_back (IPtr (unit, false)); + return true; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API EditControllerEx1::getUnitInfo (int32 unitIndex, UnitInfo& info /*out*/) +{ + Unit* unit = units.at (unitIndex); + if (unit) + { + info = unit->getInfo (); + return kResultTrue; + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult EditControllerEx1::notifyUnitSelection () +{ + tresult result = kResultFalse; + FUnknownPtr unitHandler (componentHandler); + if (unitHandler) + result = unitHandler->notifyUnitSelection (selectedUnit); + return result; +} + +//------------------------------------------------------------------------ +bool EditControllerEx1::addProgramList (ProgramList* list) +{ + programIndexMap[list->getID ()] = programLists.size (); + programLists.push_back (IPtr (list, false)); + list->addDependent (this); + return true; +} + +//------------------------------------------------------------------------ +ProgramList* EditControllerEx1::getProgramList (ProgramListID listId) const +{ + ProgramIndexMap::const_iterator it = programIndexMap.find (listId); + return it == programIndexMap.end () ? 0 : programLists[it->second]; +} + +//------------------------------------------------------------------------ +tresult EditControllerEx1::notifyProgramListChange (ProgramListID listId, int32 programIndex) +{ + tresult result = kResultFalse; + FUnknownPtr unitHandler (componentHandler); + if (unitHandler) + result = unitHandler->notifyProgramListChange (listId, programIndex); + return result; +} + +//------------------------------------------------------------------------ +int32 PLUGIN_API EditControllerEx1::getProgramListCount () +{ + return static_cast (programLists.size ()); +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API EditControllerEx1::getProgramListInfo (int32 listIndex, + ProgramListInfo& info /*out*/) +{ + if (listIndex < 0 || listIndex >= static_cast (programLists.size ())) + return kResultFalse; + info = programLists[listIndex]->getInfo (); + return kResultTrue; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API EditControllerEx1::getProgramName (ProgramListID listId, int32 programIndex, + String128 name /*out*/) +{ + ProgramIndexMap::const_iterator it = programIndexMap.find (listId); + if (it != programIndexMap.end ()) + { + return programLists[it->second]->getProgramName (programIndex, name); + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult EditControllerEx1::setProgramName (ProgramListID listId, int32 programIndex, + const String128 name /*in*/) +{ + ProgramIndexMap::const_iterator it = programIndexMap.find (listId); + if (it != programIndexMap.end ()) + { + return programLists[it->second]->setProgramName (programIndex, name); + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API EditControllerEx1::getProgramInfo (ProgramListID listId, int32 programIndex, + CString attributeId /*in*/, + String128 attributeValue /*out*/) +{ + ProgramIndexMap::const_iterator it = programIndexMap.find (listId); + if (it != programIndexMap.end ()) + { + return programLists[it->second]->getProgramInfo (programIndex, attributeId, attributeValue); + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API EditControllerEx1::hasProgramPitchNames (ProgramListID listId, + int32 programIndex) +{ + ProgramIndexMap::const_iterator it = programIndexMap.find (listId); + if (it != programIndexMap.end ()) + { + return programLists[it->second]->hasPitchNames (programIndex); + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API EditControllerEx1::getProgramPitchName (ProgramListID listId, int32 programIndex, + int16 midiPitch, String128 name /*out*/) +{ + ProgramIndexMap::const_iterator it = programIndexMap.find (listId); + if (it != programIndexMap.end ()) + { + return programLists[it->second]->getPitchName (programIndex, midiPitch, name); + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +void PLUGIN_API EditControllerEx1::update (FUnknown* changedUnknown, int32 message) +{ + ProgramList* programList = FCast (changedUnknown); + if (programList) + { + FUnknownPtr unitHandler (componentHandler); + if (unitHandler) + unitHandler->notifyProgramListChange (programList->getID (), kAllProgramInvalid); + } +} + +//------------------------------------------------------------------------ +// Unit implementation +//------------------------------------------------------------------------ +Unit::Unit () +{ + memset (&info, 0, sizeof (UnitInfo)); +} + +//------------------------------------------------------------------------ +Unit::Unit (const String128 name, UnitID unitId, UnitID parentUnitId, ProgramListID programListId) +{ + setName (name); + info.id = unitId; + info.parentUnitId = parentUnitId; + info.programListId = programListId; +} + +//------------------------------------------------------------------------ +Unit::Unit (const UnitInfo& info) +: info (info) +{ +} + +//------------------------------------------------------------------------ +void Unit::setName (const String128 newName) +{ + UString128 (newName).copyTo (info.name, 128); +} + +//------------------------------------------------------------------------ +// ProgramList implementation +//------------------------------------------------------------------------ +ProgramList::ProgramList (const String128 name, ProgramListID listId, UnitID unitId) +: unitId (unitId), parameter (0) +{ + UString128 (name).copyTo (info.name, 128); + info.id = listId; + info.programCount = 0; +} + +//------------------------------------------------------------------------ +ProgramList::ProgramList (const ProgramList& programList) +: info (programList.info) +, unitId (programList.unitId) +, programNames (programList.programNames) +, parameter (0) +{ +} + +//------------------------------------------------------------------------ +int32 ProgramList::addProgram (const String128 name) +{ + ++info.programCount; + programNames.push_back (name); + programInfos.push_back (ProgramInfoVector::value_type ()); + return static_cast (programNames.size ()) - 1; +} + +//------------------------------------------------------------------------ +bool ProgramList::setProgramInfo (int32 programIndex, CString attributeId, const String128 value) +{ + if (programIndex >= 0 && programIndex < static_cast(programNames.size ())) + { + programInfos.at (programIndex).insert (std::make_pair (attributeId, value)); + return true; + } + return false; +} + +//------------------------------------------------------------------------ +tresult ProgramList::getProgramInfo (int32 programIndex, CString attributeId, + String128 value /*out*/) +{ + if (programIndex >= 0 && programIndex < static_cast(programNames.size ())) + { + StringMap::const_iterator it = programInfos[programIndex].find (attributeId); + if (it != programInfos[programIndex].end ()) + { + if (!it->second.isEmpty ()) + { + it->second.copyTo16 (value, 0, 128); + return kResultTrue; + } + } + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult ProgramList::getProgramName (int32 programIndex, String128 name /*out*/) +{ + if (programIndex >= 0 && programIndex < static_cast(programNames.size ())) + { + programNames.at (programIndex).copyTo16 (name, 0, 128); + return kResultTrue; + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult ProgramList::setProgramName (int32 programIndex, const String128 name /*in*/) +{ + if (programIndex >= 0 && programIndex < static_cast(programNames.size ())) + { + programNames.at (programIndex) = name; + if (parameter) + { + static_cast (parameter)->replaceString (programIndex, name); + } + return kResultTrue; + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +Parameter* ProgramList::getParameter () +{ + if (parameter == 0) + { + StringListParameter* listParameter = new StringListParameter ( + info.name, info.id, 0, + ParameterInfo::kCanAutomate | ParameterInfo::kIsList | ParameterInfo::kIsProgramChange, + unitId); + for (StringVector::const_iterator it = programNames.begin (), end = programNames.end (); + it != end; ++it) + { + listParameter->appendString (*it); + } + parameter = listParameter; + } + return parameter; +} + +//------------------------------------------------------------------------ +// ProgramListWithPitchNames implementation +//----------------------------------------------------------------------------- +ProgramListWithPitchNames::ProgramListWithPitchNames (const String128 name, ProgramListID listId, + UnitID unitId) +: ProgramList (name, listId, unitId) +{ +} + +//----------------------------------------------------------------------------- +int32 ProgramListWithPitchNames::addProgram (const String128 name) +{ + int32 index = ProgramList::addProgram (name); + if (index >= 0) + pitchNames.push_back (PitchNamesVector::value_type ()); + return index; +} + +//----------------------------------------------------------------------------- +bool ProgramListWithPitchNames::setPitchName (int32 programIndex, int16 pitch, + const String128 pitchName) +{ + if (programIndex < 0 || programIndex >= getCount ()) + return false; + + bool nameChanged = true; + std::pair res = + pitchNames[programIndex].insert (std::make_pair (pitch, pitchName)); + if (!res.second) + { + if (res.first->second == pitchName) + nameChanged = false; + else + res.first->second = pitchName; + } + + if (nameChanged) + changed (); + return true; +} + +//----------------------------------------------------------------------------- +bool ProgramListWithPitchNames::removePitchName (int32 programIndex, int16 pitch) +{ + bool result = false; + if (programIndex >= 0 && programIndex < getCount ()) + { + result = pitchNames.at (programIndex).erase (pitch) != 0; + } + if (result) + changed (); + return result; +} + +//----------------------------------------------------------------------------- +tresult ProgramListWithPitchNames::hasPitchNames (int32 programIndex) +{ + if (programIndex >= 0 && programIndex < getCount ()) + return pitchNames.at (programIndex).empty () ? kResultFalse : kResultTrue; + return kResultFalse; +} + +//----------------------------------------------------------------------------- +tresult ProgramListWithPitchNames::getPitchName (int32 programIndex, int16 midiPitch, + String128 name /*out*/) +{ + if (programIndex >= 0 && programIndex < getCount ()) + { + PitchNameMap::const_iterator it = pitchNames[programIndex].find (midiPitch); + if (it != pitchNames[programIndex].end ()) + { + it->second.copyTo16 (name, 0, 128); + return kResultTrue; + } + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vsteditcontroller.h b/source/includes/vst3sdk/public.sdk/source/vst/vsteditcontroller.h new file mode 100644 index 000000000..62b542c70 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vsteditcontroller.h @@ -0,0 +1,345 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vsteditcontroller.h +// Created by : Steinberg, 04/2005 +// Description : VST Edit Controller Implementation +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "public.sdk/source/vst/vstcomponentbase.h" +#include "public.sdk/source/vst/vstparameters.h" +#include "public.sdk/source/common/pluginview.h" +#include "base/source/fstring.h" + +#include "pluginterfaces/vst/ivsteditcontroller.h" +#include "pluginterfaces/vst/ivstunits.h" + +#include +#include + +//------------------------------------------------------------------------ +namespace Steinberg { +namespace Vst { + +class EditorView; + +//------------------------------------------------------------------------ +/** Default implementation for a VST 3 edit controller. +\ingroup vstClasses +Can be used as base class for a specific controller implementation */ +//------------------------------------------------------------------------ +class EditController: public ComponentBase, + public IEditController, + public IEditController2 +{ +public: +//------------------------------------------------------------------------ + EditController (); + + //---from IEditController------- + virtual tresult PLUGIN_API setComponentState (IBStream* state) SMTG_OVERRIDE; + virtual tresult PLUGIN_API setState (IBStream* state) SMTG_OVERRIDE; + virtual tresult PLUGIN_API getState (IBStream* state) SMTG_OVERRIDE; + virtual int32 PLUGIN_API getParameterCount () SMTG_OVERRIDE; + virtual tresult PLUGIN_API getParameterInfo (int32 paramIndex, ParameterInfo& info) SMTG_OVERRIDE; + virtual tresult PLUGIN_API getParamStringByValue (ParamID tag, ParamValue valueNormalized, String128 string) SMTG_OVERRIDE; + virtual tresult PLUGIN_API getParamValueByString (ParamID tag, TChar* string, ParamValue& valueNormalized) SMTG_OVERRIDE; + virtual ParamValue PLUGIN_API normalizedParamToPlain (ParamID tag, ParamValue valueNormalized) SMTG_OVERRIDE; + virtual ParamValue PLUGIN_API plainParamToNormalized (ParamID tag, ParamValue plainValue) SMTG_OVERRIDE; + virtual ParamValue PLUGIN_API getParamNormalized (ParamID tag) SMTG_OVERRIDE; + virtual tresult PLUGIN_API setParamNormalized (ParamID tag, ParamValue value) SMTG_OVERRIDE; + virtual tresult PLUGIN_API setComponentHandler (IComponentHandler* handler) SMTG_OVERRIDE; + virtual IPlugView* PLUGIN_API createView (FIDString /*name*/) SMTG_OVERRIDE {return 0;} + + //---from IEditController2------- + virtual tresult PLUGIN_API setKnobMode (KnobMode mode) SMTG_OVERRIDE { hostKnobMode = mode; return kResultTrue; } + virtual tresult PLUGIN_API openHelp (TBool /*onlyCheck*/) SMTG_OVERRIDE {return kResultFalse;} + virtual tresult PLUGIN_API openAboutBox (TBool /*onlyCheck*/) SMTG_OVERRIDE {return kResultFalse;} + + //---from ComponentBase--------- + virtual tresult PLUGIN_API initialize (FUnknown* context) SMTG_OVERRIDE; + virtual tresult PLUGIN_API terminate () SMTG_OVERRIDE; + + //---Internal Methods------- + virtual tresult beginEdit (ParamID tag); ///< to be called before a serie of performEdit + virtual tresult performEdit (ParamID tag, ParamValue valueNormalized); ///< will inform the host about the value change + virtual tresult endEdit (ParamID tag); ///< to be called after a serie of performEdit + virtual tresult startGroupEdit (); ///< calls IComponentHandler2::startGroupEdit() if host supports it + virtual tresult finishGroupEdit (); ///< calls IComponentHandler2::finishGroupEdit() if host supports it + + virtual void editorDestroyed (EditorView* /*editor*/) {} ///< called from EditorView if it was destroyed + virtual void editorAttached (EditorView* /*editor*/) {} ///< called from EditorView if it was attached to a parent + virtual void editorRemoved (EditorView* /*editor*/) {} ///< called from EditorView if it was removed from a parent + + static KnobMode getHostKnobMode () { return hostKnobMode; } ///< return host knob mode + + /** Gets for a given tag the parameter object. */ + virtual Parameter* getParameterObject (ParamID tag) { return parameters.getParameter (tag); } + + /** Gets for a given tag the parameter information. */ + virtual tresult getParameterInfoByTag (ParamID tag, ParameterInfo& info); + + /** Calls IComponentHandler2::setDirty (state) if host supports it. */ + virtual tresult setDirty (TBool state); + + /** Calls IComponentHandler2::requestOpenEditor (name) if host supports it. */ + virtual tresult requestOpenEditor (FIDString name = ViewType::kEditor); + + //---Accessor Methods------- + IComponentHandler* getComponentHandler () const { return componentHandler; } + + //---Interface--------- + OBJ_METHODS (EditController, ComponentBase) + DEFINE_INTERFACES + DEF_INTERFACE (IEditController) + DEF_INTERFACE (IEditController2) + END_DEFINE_INTERFACES (ComponentBase) + REFCOUNT_METHODS(ComponentBase) +//------------------------------------------------------------------------ +protected: + IComponentHandler* componentHandler; + IComponentHandler2* componentHandler2; + + ParameterContainer parameters; + + static KnobMode hostKnobMode; +}; + + +//------------------------------------------------------------------------ +/** View related to an edit controller. +\ingroup vstClasses */ +//------------------------------------------------------------------------ +class EditorView: public CPluginView +{ +public: +//------------------------------------------------------------------------ + EditorView (EditController* controller, ViewRect* size = 0); + virtual ~EditorView (); + + /** Gets its controller part. */ + EditController* getController () { return controller; } + + //---from CPluginView------------- + virtual void attachedToParent () SMTG_OVERRIDE; + virtual void removedFromParent () SMTG_OVERRIDE; + +//------------------------------------------------------------------------ +protected: + EditController* controller; +}; + + +//------------------------------------------------------------------------ +/** Unit element. +\ingroup vstClasses */ +//------------------------------------------------------------------------ +class Unit: public FObject +{ +public: +//------------------------------------------------------------------------ + Unit (const String128 name, UnitID unitId, UnitID parentUnitId = kRootUnitId, ProgramListID programListId = kNoProgramListId); + Unit (const UnitInfo& unit); + + /** Returns its info. */ + const UnitInfo& getInfo () const {return info;} + + /** Returns its Unit ID. */ + UnitID getID () const {return info.id;} + + /** Sets a new Unit ID. */ + void setID (UnitID newID) {info.id = newID;} + + /** Returns its Unit Name. */ + const TChar* getName () const {return info.name;} + + /** Sets a new Unit Name. */ + void setName (const String128 newName); + + /** Returns its ProgramList ID. */ + ProgramListID getProgramListID () const {return info.programListId;} + + /** Sets a new ProgramList ID. */ + void setProgramListID (ProgramListID newID) {info.programListId = newID;} + + OBJ_METHODS (Unit, FObject) +//------------------------------------------------------------------------ +protected: + Unit (); + UnitInfo info; +}; + +//------------------------------------------------------------------------ +/** ProgramList element. +\ingroup vstClasses */ +//------------------------------------------------------------------------ +class ProgramList: public FObject +{ +public: +//------------------------------------------------------------------------ + ProgramList (const String128 name, ProgramListID listId, UnitID unitId); + ProgramList (const ProgramList& programList); + + const ProgramListInfo& getInfo () const { return info; } + ProgramListID getID () const { return info.id; } + const TChar* getName () const { return info.name; } + int32 getCount () const { return info.programCount; } + + virtual tresult getProgramName (int32 programIndex, String128 name /*out*/); + virtual tresult setProgramName (int32 programIndex, const String128 name /*in*/); + virtual tresult getProgramInfo (int32 programIndex, CString attributeId, String128 value /*out*/); + virtual tresult hasPitchNames (int32 programIndex) { return kResultFalse; } + virtual tresult getPitchName (int32 programIndex, int16 midiPitch, String128 name /*out*/) { return kResultFalse; } + + /** Adds a program to the end of the list. returns the index of the program. */ + virtual int32 addProgram (const String128 name); + + /** Sets a program attribute value. */ + virtual bool setProgramInfo (int32 programIndex, CString attributeId, const String128 value); + + /** Creates and returns the program parameter. */ + virtual Parameter* getParameter (); + + OBJ_METHODS (ProgramList, FObject) +//------------------------------------------------------------------------ +protected: + typedef std::map StringMap; + typedef std::vector StringVector; + typedef std::vector ProgramInfoVector; + ProgramListInfo info; + UnitID unitId; + StringVector programNames; + ProgramInfoVector programInfos; + Parameter* parameter; +}; + +//------------------------------------------------------------------------ +/** ProgramListWithPitchNames element. +\ingroup vstClasses */ +//----------------------------------------------------------------------------- +class ProgramListWithPitchNames: public ProgramList +{ +public: + ProgramListWithPitchNames (const String128 name, ProgramListID listId, UnitID unitId); + + /** Sets a name for the given program index and a given pitch. */ + bool setPitchName (int32 programIndex, int16 pitch, const String128 pitchName); + + /** Removes the PitchName entry for the given program index and a given pitch. Returns true if it was found and removed. */ + bool removePitchName (int32 programIndex, int16 pitch); + + //---from ProgramList--------- + int32 addProgram (const String128 name) SMTG_OVERRIDE; + tresult hasPitchNames (int32 programIndex) SMTG_OVERRIDE; + tresult getPitchName (int32 programIndex, int16 midiPitch, String128 name /*out*/) SMTG_OVERRIDE; + + OBJ_METHODS (ProgramListWithPitchNames, ProgramList) +protected: + typedef std::map PitchNameMap; + typedef std::vector PitchNamesVector; + PitchNamesVector pitchNames; +}; + + +//------------------------------------------------------------------------ +/** Advanced implementation (support IUnitInfo) for a VST 3 edit controller. +\ingroup vstClasses +- [extends EditController] +*/ +//------------------------------------------------------------------------ +class EditControllerEx1: public EditController, public IUnitInfo +{ +public: + EditControllerEx1 (); + virtual ~EditControllerEx1 (); + + /** Adds a given unit. */ + bool addUnit (Unit* unit); + + /** Adds a given program list. */ + bool addProgramList (ProgramList* list); + + /** Returns the ProgramList associated to a given listId. */ + ProgramList* getProgramList (ProgramListID listId) const; + + /** Notifies the host about program list changes. */ + tresult notifyProgramListChange (ProgramListID listId, int32 programIndex = kAllProgramInvalid); + + //---from IUnitInfo------------------ + virtual int32 PLUGIN_API getUnitCount () SMTG_OVERRIDE { return static_cast (units.size ()); } + virtual tresult PLUGIN_API getUnitInfo (int32 unitIndex, UnitInfo& info /*out*/) SMTG_OVERRIDE; + + virtual int32 PLUGIN_API getProgramListCount () SMTG_OVERRIDE; + virtual tresult PLUGIN_API getProgramListInfo (int32 listIndex, ProgramListInfo& info /*out*/) SMTG_OVERRIDE; + virtual tresult PLUGIN_API getProgramName (ProgramListID listId, int32 programIndex, String128 name /*out*/) SMTG_OVERRIDE; + virtual tresult PLUGIN_API getProgramInfo (ProgramListID listId, int32 programIndex, CString attributeId /*in*/, String128 attributeValue /*out*/) SMTG_OVERRIDE; + + virtual tresult PLUGIN_API hasProgramPitchNames (ProgramListID listId, int32 programIndex) SMTG_OVERRIDE; + virtual tresult PLUGIN_API getProgramPitchName (ProgramListID listId, int32 programIndex, int16 midiPitch, String128 name /*out*/) SMTG_OVERRIDE; + + virtual tresult setProgramName (ProgramListID listId, int32 programIndex, const String128 name /*in*/); + + // units selection -------------------- + virtual UnitID PLUGIN_API getSelectedUnit () SMTG_OVERRIDE {return selectedUnit;} + virtual tresult PLUGIN_API selectUnit (UnitID unitId) SMTG_OVERRIDE {selectedUnit = unitId; return kResultTrue;} + + virtual tresult PLUGIN_API getUnitByBus (MediaType /*type*/, BusDirection /*dir*/, int32 /*busIndex*/, + int32 /*channel*/, UnitID& /*unitId*/ /*out*/) SMTG_OVERRIDE {return kResultFalse;} + virtual tresult PLUGIN_API setUnitProgramData (int32 /*listOrUnitId*/, int32 /*programIndex*/, IBStream* /*data*/) SMTG_OVERRIDE {return kResultFalse;} + + /** Notifies the host about the selected Unit. */ + virtual tresult notifyUnitSelection (); + + //---from IDependent------------------ + virtual void PLUGIN_API update (FUnknown* changedUnknown, int32 message) SMTG_OVERRIDE; + + //---Interface--------- + OBJ_METHODS (EditControllerEx1, EditController) + DEFINE_INTERFACES + DEF_INTERFACE (IUnitInfo) + END_DEFINE_INTERFACES (EditController) + REFCOUNT_METHODS(EditController) + +protected: + typedef std::vector > ProgramListVector; + typedef std::map ProgramIndexMap; + typedef std::vector > UnitVector; + UnitVector units; + ProgramListVector programLists; + ProgramIndexMap programIndexMap; + UnitID selectedUnit; +}; + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vstguieditor.cpp b/source/includes/vst3sdk/public.sdk/source/vst/vstguieditor.cpp new file mode 100644 index 000000000..a90d86ca6 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vstguieditor.cpp @@ -0,0 +1,394 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vstguieditor.cpp +// Created by : Steinberg, 04/2005 +// Description : VSTGUI Editor +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "vstguieditor.h" +#include "pluginterfaces/base/keycodes.h" + +#if VSTGUI_VERSION_MAJOR < 4 +#include "vstgui/vstkeycode.h" +#endif + +#include "base/source/fstring.h" + +#if MAC +#include +#include +namespace VSTGUI { + static void CreateVSTGUIBundleRef (); + static void ReleaseVSTGUIBundleRef (); +} +#elif WINDOWS +void* hInstance = 0; // VSTGUI hInstance +extern void* moduleHandle; +#elif LINUX +extern void* moduleHandle; +namespace VSTGUI { + void* soHandle = nullptr; +} // VSTGUI +#endif // MAC + +namespace Steinberg { +namespace Vst { + +//------------------------------------------------------------------------ +VSTGUIEditor::VSTGUIEditor (void* controller, ViewRect* size) +: EditorView (static_cast(controller), size) +{ + #if MAC + CreateVSTGUIBundleRef (); + #elif WINDOWS + hInstance = moduleHandle; + #elif LINUX + VSTGUI::soHandle = moduleHandle; + #endif + // create a timer used for idle update: will call notify method + timer = new CVSTGUITimer (dynamic_cast(this)); +} + +//------------------------------------------------------------------------ +VSTGUIEditor::~VSTGUIEditor () +{ + if (timer) + timer->forget (); + #if MAC + ReleaseVSTGUIBundleRef (); + #endif +} + +//------------------------------------------------------------------------ +void VSTGUIEditor::setIdleRate (int32 millisec) +{ + if (timer) + timer->setFireTime (millisec); +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API VSTGUIEditor::isPlatformTypeSupported (FIDString type) +{ +#if WINDOWS + if (strcmp (type, kPlatformTypeHWND) == 0) + return kResultTrue; + +#elif MAC + #if TARGET_OS_IPHONE + if (strcmp (type, kPlatformTypeUIView) == 0) + return kResultTrue; + #else + #if MAC_CARBON + if (strcmp (type, kPlatformTypeHIView) == 0) + return kResultTrue; + #endif + + #if MAC_COCOA + if (strcmp (type, kPlatformTypeNSView) == 0) + return kResultTrue; + #endif + #endif +#elif LINUX + if (strcmp (type, kPlatformTypeX11EmbedWindowID) == 0) + return kResultTrue; +#endif + + return kInvalidArgument; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API VSTGUIEditor::attached (void* parent, FIDString type) +{ +#if MAC + if (isPlatformTypeSupported (type) != kResultTrue) + return kResultFalse; + + #if MAC_COCOA && MAC_CARBON && !(VSTGUI_VERSION_MAJOR >= 4 && VSTGUI_VERSION_MINOR >= 1) + CFrame::setCocoaMode (strcmp (type, kPlatformTypeNSView) == 0); + #endif +#endif + + if (timer) + timer->start (); + +#if VSTGUI_VERSION_MAJOR >= 4 && VSTGUI_VERSION_MINOR >= 1 + PlatformType platformType = kDefaultNative; +#if MAC + #if TARGET_OS_IPHONE + if (strcmp (type, kPlatformTypeUIView) == 0) + platformType = kUIView; + #else + #if MAC_CARBON + if (strcmp (type, kPlatformTypeHIView) == 0) + platformType = kWindowRef; + #endif + + #if MAC_COCOA + if (strcmp (type, kPlatformTypeNSView) == 0) + platformType = kNSView; + #endif + #endif +#endif // MAC + if (open (parent, platformType) == true) +#else + if (open (parent) == true) +#endif + { + ViewRect vr (0, 0, (int32)frame->getWidth (), (int32)frame->getHeight ()); + setRect (vr); + if (plugFrame) + plugFrame->resizeView (this, &vr); + } + return EditorView::attached (parent, type); +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API VSTGUIEditor::removed () +{ + if (timer) + timer->stop (); + + close (); + return EditorView::removed (); +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API VSTGUIEditor::onSize (ViewRect* newSize) +{ + if (frame) + frame->setSize (newSize->right - newSize->left, newSize->bottom - newSize->top); + + return EditorView::onSize (newSize); +} + +//------------------------------------------------------------------------ +void VSTGUIEditor::beginEdit (VSTGUI_INT32 index) +{ + if (controller) + controller->beginEdit (index); +} + +//------------------------------------------------------------------------ +void VSTGUIEditor::endEdit (VSTGUI_INT32 index) +{ + if (controller) + controller->endEdit (index); +} + +//------------------------------------------------------------------------ +VSTGUI_INT32 VSTGUIEditor::getKnobMode () const +{ + switch (EditController::getHostKnobMode ()) + { + case kRelativCircularMode: return VSTGUI::kRelativCircularMode; + case kLinearMode: return VSTGUI::kLinearMode; + } + return VSTGUI::kCircularMode; +} + +//------------------------------------------------------------------------ +CMessageResult VSTGUIEditor::notify (CBaseObject* /*sender*/, const char* message) +{ + if (message == CVSTGUITimer::kMsgTimer) + { + if (frame) + frame->idle (); + + return kMessageNotified; + } + + return kMessageUnknown; +} + +//------------------------------------------------------------------------ +static bool translateKeyMessage (VstKeyCode& keyCode, char16 key, int16 keyMsg, int16 modifiers) +{ + keyCode.character = 0; + keyCode.virt = (unsigned char)keyMsg; + keyCode.modifier = 0; + if (key == 0) + key = VirtualKeyCodeToChar ((uint8)keyMsg); + if (key) + { + String keyStr (STR(" ")); + keyStr.setChar16 (0, key); + keyStr.toMultiByte (kCP_Utf8); + if (keyStr.length () == 1) + keyCode.character = keyStr.getChar8 (0); + } + if (modifiers) + { + if (modifiers & kShiftKey) + keyCode.modifier |= MODIFIER_SHIFT; + if (modifiers & kAlternateKey) + keyCode.modifier |= MODIFIER_ALTERNATE; + if (modifiers & kCommandKey) + keyCode.modifier |= MODIFIER_CONTROL; + if (modifiers & kControlKey) + keyCode.modifier |= MODIFIER_COMMAND; + } + return true; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API VSTGUIEditor::onKeyDown (char16 key, int16 keyMsg, int16 modifiers) +{ + if (frame) + { + VstKeyCode keyCode = {0}; + if (translateKeyMessage (keyCode, key, keyMsg, modifiers)) + { + VSTGUI_INT32 result = frame->onKeyDown (keyCode); + if (result == 1) + return kResultTrue; + } + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API VSTGUIEditor::onKeyUp (char16 key, int16 keyMsg, int16 modifiers) +{ + if (frame) + { + VstKeyCode keyCode = {0}; + if (translateKeyMessage (keyCode, key, keyMsg, modifiers)) + { + VSTGUI_INT32 result = frame->onKeyUp (keyCode); + if (result == 1) + return kResultTrue; + } + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API VSTGUIEditor::onWheel (float distance) +{ + if (frame) + { + CPoint where; + frame->getCurrentMouseLocation (where); + if (frame->onWheel (where, distance, frame->getCurrentMouseButtons ())) + return kResultTrue; + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API VSTGUIEditor::setFrame (IPlugFrame* frame) +{ + if (frame) + { + FUnknownPtr frameIdle (frame); + if (frameIdle) + frameIdle->addIdleHandler (this); + + } + else if (plugFrame) + { + FUnknownPtr frameIdle (plugFrame); + if (frameIdle) + frameIdle->removeIdleHandler (this); + } + return EditorView::setFrame (frame); +} + +//------------------------------------------------------------------------ +void PLUGIN_API VSTGUIEditor::onPlugViewIdle () +{ + frame->handleNextSystemEvents (); +} + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg + +#if MAC +namespace VSTGUI { +void* gBundleRef = 0; +static int openCount = 0; +//------------------------------------------------------------------------ +void CreateVSTGUIBundleRef () +{ + openCount++; + if (gBundleRef) + { + CFRetain (gBundleRef); + return; + } +#if TARGET_OS_IPHONE + gBundleRef = CFBundleGetMainBundle (); + CFRetain (gBundleRef); +#else + Dl_info info; + if (dladdr ((const void*)CreateVSTGUIBundleRef, &info)) + { + if (info.dli_fname) + { + Steinberg::String name; + name.assign (info.dli_fname); + for (int i = 0; i < 3; i++) + { + int delPos = name.findLast ('/'); + if (delPos == -1) + { + fprintf (stdout, "Could not determine bundle location.\n"); + return; // unexpected + } + name.remove (delPos, name.length () - delPos); + } + CFURLRef bundleUrl = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*)name.text8 (), name.length (), true); + if (bundleUrl) + { + gBundleRef = CFBundleCreate (0, bundleUrl); + CFRelease (bundleUrl); + } + } + } +#endif +} + +//------------------------------------------------------------------------ +void ReleaseVSTGUIBundleRef () +{ + openCount--; + if (gBundleRef) + CFRelease (gBundleRef); + if (openCount == 0) + gBundleRef = 0; +} + +//------------------------------------------------------------------------ +} // namespace VSTGUI + +#endif // MAC diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vstguieditor.h b/source/includes/vst3sdk/public.sdk/source/vst/vstguieditor.h new file mode 100644 index 000000000..5a68afda7 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vstguieditor.h @@ -0,0 +1,120 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vstguieditor.h +// Created by : Steinberg, 04/2005 +// Description : VSTGUI Editor +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "vsteditcontroller.h" +#include "vstgui/vstgui.h" + +#if VSTGUI_VERSION_MAJOR < 4 +#include "vstgui/cvstguitimer.h" +#define VSTGUI_INT32 long +#else +#define VSTGUI_INT32 int32_t +#endif + +namespace Steinberg { +namespace Vst { + +//------------------------------------------------------------------------ +/** Base class for an edit view using VSTGUI. +\ingroup vstClasses */ +//------------------------------------------------------------------------ +class VSTGUIEditor : public EditorView, public VSTGUIEditorInterface, public CBaseObject, public IPlugViewIdleHandler +{ +public: + /** Constructor. */ + VSTGUIEditor (void* controller, ViewRect* size = 0); + + /** Destructor. */ + virtual ~VSTGUIEditor (); + + //---Internal function----- + /** Called when the editor will be opened. */ +#if VSTGUI_VERSION_MAJOR >= 4 && VSTGUI_VERSION_MINOR >= 1 + virtual bool PLUGIN_API open (void* parent, const PlatformType& platformType = kDefaultNative) = 0; +#else + virtual bool PLUGIN_API open (void* parent) = 0; +#endif + /** Called when the editor will be closed. */ + virtual void PLUGIN_API close () = 0; + + /** Sets the idle rate controlling the parameter update rate. */ + void setIdleRate (int32 millisec); + + //---from CBaseObject--------------- + CMessageResult notify (CBaseObject* sender, const char* message) SMTG_OVERRIDE; + void forget () SMTG_OVERRIDE { EditorView::release (); } + void remember () SMTG_OVERRIDE { EditorView::addRef (); } + VSTGUI_INT32 getNbReference () const SMTG_OVERRIDE { return refCount; } + + //---from IPlugView------- + tresult PLUGIN_API isPlatformTypeSupported (FIDString type) SMTG_OVERRIDE; + tresult PLUGIN_API onSize (ViewRect* newSize) SMTG_OVERRIDE; + + //---from VSTGUIEditorInterface------- + /** Called from VSTGUI when a user begins editing. + The default implementation calls performEdit of the EditController. */ + void beginEdit (VSTGUI_INT32 index) SMTG_OVERRIDE; + /** Called from VSTGUI when a user ends editing. + The default implementation calls endEdit of the EditController. */ + void endEdit (VSTGUI_INT32 index) SMTG_OVERRIDE; + + VSTGUI_INT32 getKnobMode () const SMTG_OVERRIDE; + + OBJ_METHODS (VSTGUIEditor, EditorView) + DEFINE_INTERFACES + DEF_INTERFACE (IPlugViewIdleHandler) + END_DEFINE_INTERFACES (EditorView) + REFCOUNT_METHODS(EditorView) +private: + //---from IPlugView------- + tresult PLUGIN_API attached (void* parent, FIDString type) SMTG_OVERRIDE; + tresult PLUGIN_API removed () SMTG_OVERRIDE; + tresult PLUGIN_API onKeyDown (char16 key, int16 keyMsg, int16 modifiers) SMTG_OVERRIDE; + tresult PLUGIN_API onKeyUp (char16 key, int16 keyMsg, int16 modifiers) SMTG_OVERRIDE; + tresult PLUGIN_API onWheel (float distance) SMTG_OVERRIDE; + tresult PLUGIN_API setFrame (IPlugFrame* frame) SMTG_OVERRIDE; + + //---from IPlugViewIdle------- + void PLUGIN_API onPlugViewIdle () SMTG_OVERRIDE; + + CVSTGUITimer* timer; +}; + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vstinitiids.cpp b/source/includes/vst3sdk/public.sdk/source/vst/vstinitiids.cpp new file mode 100644 index 000000000..dc1818af1 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vstinitiids.cpp @@ -0,0 +1,116 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vstinitiids.cpp +// Created by : Steinberg, 10/2009 +// Description : Interface symbols file +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "pluginterfaces/base/funknown.h" + +#include "pluginterfaces/vst/ivsteditcontroller.h" +#include "pluginterfaces/vst/ivstmessage.h" +#include "pluginterfaces/vst/ivstunits.h" +#include "pluginterfaces/vst/ivstaudioprocessor.h" +#include "pluginterfaces/vst/ivstparameterchanges.h" +#include "pluginterfaces/vst/ivstevents.h" +#include "pluginterfaces/vst/ivsthostapplication.h" +#include "pluginterfaces/vst/ivstplugview.h" +#include "pluginterfaces/vst/ivstcontextmenu.h" +#include "pluginterfaces/vst/ivstrepresentation.h" +#include "pluginterfaces/vst/ivstinterappaudio.h" +#include "pluginterfaces/vst/ivstchannelcontextinfo.h" +#include "pluginterfaces/vst/ivstprefetchablesupport.h" +#include "pluginterfaces/vst/ivstautomationstate.h" + +namespace Steinberg { +namespace Vst { + + //----VST 3.0-------------------------------- + DEF_CLASS_IID (IComponent) + DEF_CLASS_IID (IAudioProcessor) + DEF_CLASS_IID (IUnitData) + DEF_CLASS_IID (IProgramListData) + + DEF_CLASS_IID (IEditController) + DEF_CLASS_IID (IUnitInfo) + + DEF_CLASS_IID (IConnectionPoint) + + DEF_CLASS_IID (IComponentHandler) + DEF_CLASS_IID (IUnitHandler) + + DEF_CLASS_IID (IParamValueQueue) + DEF_CLASS_IID (IParameterChanges) + + DEF_CLASS_IID (IEventList) + DEF_CLASS_IID (IMessage) + + DEF_CLASS_IID (IHostApplication) + DEF_CLASS_IID (IAttributeList) + + //----VST 3.0.1-------------------------------- + DEF_CLASS_IID (IMidiMapping) + + //----VST 3.0.2-------------------------------- + DEF_CLASS_IID (IParameterFinder) + + //----VST 3.1---------------------------------- + DEF_CLASS_IID (IComponentHandler2) + DEF_CLASS_IID (IEditController2) + DEF_CLASS_IID (IAudioPresentationLatency) + + //---VST 3.1 for AU and VST 2 Wrapper----------- + DEF_CLASS_IID (IVst3ToVst2Wrapper) + DEF_CLASS_IID (IVst3ToAUWrapper) + + //----VST 3.5---------------------------------- + DEF_CLASS_IID (INoteExpressionController) + DEF_CLASS_IID (IKeyswitchController) + DEF_CLASS_IID (IContextMenuTarget) + DEF_CLASS_IID (IContextMenu) + DEF_CLASS_IID (IComponentHandler3) + DEF_CLASS_IID (IEditControllerHostEditing) + DEF_CLASS_IID (IXmlRepresentationController) + + //----VST 3.6---------------------------------- + DEF_CLASS_IID (IInterAppAudioHost) + DEF_CLASS_IID (IInterAppAudioConnectionNotification) + DEF_CLASS_IID (IInterAppAudioPresetManager) + DEF_CLASS_IID (IStreamAttributes) + + //----VST 3.6.5-------------------------------- + DEF_CLASS_IID (ChannelContext::IInfoListener) + DEF_CLASS_IID (IPrefetchableSupport) + DEF_CLASS_IID (IUnitHandler2) + DEF_CLASS_IID (IAutomationState) +} +} diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vstnoteexpressiontypes.cpp b/source/includes/vst3sdk/public.sdk/source/vst/vstnoteexpressiontypes.cpp new file mode 100644 index 000000000..4a4df85f3 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vstnoteexpressiontypes.cpp @@ -0,0 +1,293 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vstnoteexpressiontypes.cpp +// Created by : Steinberg, 12/2010 +// Description : VST Note Expression Type Info Implementation +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "vstnoteexpressiontypes.h" +#include "vstparameters.h" +#include "pluginterfaces/base/ustring.h" +#include "pluginterfaces/base/futils.h" +#include "base/source/fstring.h" + +#include + +namespace Steinberg { +namespace Vst { + +//----------------------------------------------------------------------------- +NoteExpressionType::NoteExpressionType () +: precision (4) +{ + memset (&info, 0, sizeof (info)); +} + +//----------------------------------------------------------------------------- +NoteExpressionType::NoteExpressionType (const NoteExpressionTypeInfo& _info) +: precision (4) +{ + memcpy (&info, &_info, sizeof (info)); +} + +//----------------------------------------------------------------------------- +NoteExpressionType::NoteExpressionType (NoteExpressionTypeID _typeId, const TChar* _title, const TChar* _shortTitle, const TChar* _units, + int32 _unitId, NoteExpressionValue _defaultValue, NoteExpressionValue _minimum, NoteExpressionValue _maximum, + int32 _stepCount, int32 _flags, int32 _precision) +: precision (_precision) +{ + memset (&info, 0, sizeof (info)); + info.typeId = _typeId; + if (_title) + UString (info.title, str16BufferSize (String128)).assign (_title); + if (_shortTitle) + UString (info.shortTitle, str16BufferSize (String128)).assign (_shortTitle); + if (_units) + UString (info.shortTitle, str16BufferSize (String128)).assign (_units); + info.unitId = _unitId; + info.valueDesc.defaultValue = _defaultValue; + info.valueDesc.minimum = _minimum; + info.valueDesc.maximum = _maximum; + info.valueDesc.stepCount = _stepCount; + info.flags = _flags; +} + +//----------------------------------------------------------------------------- +NoteExpressionType::NoteExpressionType (NoteExpressionTypeID _typeId, const TChar* _title, const TChar* _shortTitle, const TChar* _units, + int32 _unitId, Parameter* _associatedParameter, int32 _flags) +: associatedParameter (_associatedParameter) +, precision (4) +{ + memset (&info, 0, sizeof (info)); + info.typeId = _typeId; + if (_title) + UString (info.title, str16BufferSize (String128)).assign (_title); + if (_shortTitle) + UString (info.shortTitle, str16BufferSize (String128)).assign (_shortTitle); + if (_units) + UString (info.shortTitle, str16BufferSize (String128)).assign (_units); + info.unitId = _unitId; + info.valueDesc.defaultValue = 0.5; + info.valueDesc.minimum = 0.; + info.valueDesc.maximum = 1.; + info.flags = _flags; + if (_associatedParameter) + { + info.valueDesc.stepCount = _associatedParameter->getInfo ().stepCount; + info.valueDesc.defaultValue = _associatedParameter->getInfo ().defaultNormalizedValue; + info.associatedParameterId = associatedParameter->getInfo ().id; + info.flags |= NoteExpressionTypeInfo::kAssociatedParameterIDValid; + } +} + +//----------------------------------------------------------------------------- +tresult NoteExpressionType::getStringByValue (NoteExpressionValue valueNormalized /*in*/, String128 string /*out*/) +{ + if (associatedParameter) + { + associatedParameter->toString (valueNormalized, string); + return kResultTrue; + } + UString128 wrapper; + if (info.valueDesc.stepCount > 0) + { + int32 value = Min (info.valueDesc.stepCount, (int32)(valueNormalized * (info.valueDesc.stepCount + 1))); + wrapper.printInt (value); + } + else + { + wrapper.printFloat (valueNormalized, precision); + } + wrapper.copyTo (string, 128); + return kResultTrue; +} + +//----------------------------------------------------------------------------- +tresult NoteExpressionType::getValueByString (const TChar* string /*in*/, NoteExpressionValue& valueNormalized /*out*/) +{ + if (associatedParameter) + { + return associatedParameter->fromString (string, valueNormalized) ? kResultTrue : kResultFalse; + } + String wrapper (string); + if (info.valueDesc.stepCount > 0) + { + int32 value; + if (wrapper.scanInt32 (value) && value <= info.valueDesc.stepCount) + { + valueNormalized = (NoteExpressionValue)value / (NoteExpressionValue)info.valueDesc.stepCount; + return kResultTrue; + } + return kResultFalse; + } + double value; + wrapper.scanFloat (value); + if (value < info.valueDesc.minimum) + return kResultFalse; + if (value > info.valueDesc.maximum) + return kResultFalse; + valueNormalized = value; + return kResultTrue; +} + +//----------------------------------------------------------------------------- +RangeNoteExpressionType::RangeNoteExpressionType (NoteExpressionTypeID _typeId, const TChar* _title, const TChar* _shortTitle, const TChar* _units, + int32 _unitId, NoteExpressionValue _defaultPlainValue, NoteExpressionValue _plainMin, NoteExpressionValue _plainMax, + int32 _flags, int32 _precision) +: NoteExpressionType (_typeId, _title, _shortTitle, _units, _unitId, 0, 0, 1, 0, _flags, _precision) +, plainMin (_plainMin) +, plainMax (_plainMax) +{ + info.valueDesc.defaultValue = (_defaultPlainValue - getMin ()) / (getMax () - getMin ()); +} + +//----------------------------------------------------------------------------- +tresult RangeNoteExpressionType::getStringByValue (NoteExpressionValue valueNormalized /*in*/, String128 string /*out*/) +{ + NoteExpressionValue plain = valueNormalized * (getMax () - getMin ()) + getMin (); + UString128 wrapper; + wrapper.printFloat (plain, precision); + wrapper.copyTo (string, 128); + return kResultTrue; +} + +//----------------------------------------------------------------------------- +tresult RangeNoteExpressionType::getValueByString (const TChar* string /*in*/, NoteExpressionValue& valueNormalized /*out*/) +{ + String wrapper (string); + double value = 0; + if (wrapper.scanFloat (value)) + { + value = (value - getMin ()) / (getMax () - getMin ()); + if (value >= 0. && value <= 1.) + { + valueNormalized = value; + return kResultTrue; + } + } + return kResultFalse; +} + +//----------------------------------------------------------------------------- +NoteExpressionTypeContainer::NoteExpressionTypeContainer () +{ +} + +//----------------------------------------------------------------------------- +NoteExpressionTypeContainer::NoteExprTypeVector::const_iterator NoteExpressionTypeContainer::find ( + NoteExpressionTypeID typeId) const +{ + for (NoteExprTypeVector::const_iterator it = noteExps.begin (), end = noteExps.end (); + it != end; ++it) + { + if ((*it)->getInfo ().typeId == typeId) + { + return it; + } + } + return noteExps.end (); +} + +//----------------------------------------------------------------------------- +bool NoteExpressionTypeContainer::addNoteExpressionType (NoteExpressionType* noteExpType) +{ + noteExps.push_back(IPtr (noteExpType, false)); + return true; +} + +//----------------------------------------------------------------------------- +bool NoteExpressionTypeContainer::removeNoteExpressionType (NoteExpressionTypeID typeId) +{ + NoteExprTypeVector::const_iterator it = find (typeId); + if (it != noteExps.end ()) + { + noteExps.erase (it); + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +void NoteExpressionTypeContainer::removeAll () +{ + noteExps.clear (); +} + +//----------------------------------------------------------------------------- +NoteExpressionType* NoteExpressionTypeContainer::getNoteExpressionType (NoteExpressionTypeID typeId) +{ + NoteExprTypeVector::const_iterator it = find (typeId); + if (it != noteExps.end ()) + return (*it); + return 0; +} + +//----------------------------------------------------------------------------- +int32 NoteExpressionTypeContainer::getNoteExpressionCount () +{ + return static_cast (noteExps.size ()); +} + +//----------------------------------------------------------------------------- +tresult NoteExpressionTypeContainer::getNoteExpressionInfo (int32 noteExpressionIndex, NoteExpressionTypeInfo& info /*out*/) +{ + if (noteExpressionIndex < 0 || noteExpressionIndex >= static_cast (noteExps.size ())) + return kInvalidArgument; + std::memcpy (&info, ¬eExps[noteExpressionIndex]->getInfo (), sizeof (info)); + return kResultTrue; +} + +//----------------------------------------------------------------------------- +tresult NoteExpressionTypeContainer::getNoteExpressionStringByValue (NoteExpressionTypeID id, NoteExpressionValue valueNormalized /*in*/, String128 string /*out*/) +{ + NoteExpressionType* noteExpType = getNoteExpressionType (id); + if (noteExpType) + { + return noteExpType->getStringByValue (valueNormalized, string); + } + return kResultFalse; +} + +//----------------------------------------------------------------------------- +tresult NoteExpressionTypeContainer::getNoteExpressionValueByString (NoteExpressionTypeID id, const TChar* string /*in*/, NoteExpressionValue& valueNormalized /*out*/) +{ + NoteExpressionType* noteExpType = getNoteExpressionType (id); + if (noteExpType) + { + return noteExpType->getValueByString (string, valueNormalized); + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vstnoteexpressiontypes.h b/source/includes/vst3sdk/public.sdk/source/vst/vstnoteexpressiontypes.h new file mode 100644 index 000000000..94cb9612e --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vstnoteexpressiontypes.h @@ -0,0 +1,153 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vstnoteexpressiontypes.h +// Created by : Steinberg, 12/2010 +// Description : VST Note Expression Type Info Implementation +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "pluginterfaces/vst/ivstnoteexpression.h" +#include "base/source/fobject.h" + +#include +#include + +namespace Steinberg { +namespace Vst { +class Parameter; + +//------------------------------------------------------------------------ +/** Note expression type object. +\ingroup vstClasses */ +//----------------------------------------------------------------------------- +class NoteExpressionType : public FObject +{ +public: + NoteExpressionType (); + NoteExpressionType (const NoteExpressionTypeInfo& info); + NoteExpressionType (NoteExpressionTypeID typeId, const TChar* title, const TChar* shortTitle, const TChar* units, + int32 unitId, NoteExpressionValue defaultValue, NoteExpressionValue minimum, NoteExpressionValue maximum, + int32 stepCount, int32 flags = 0, int32 precision = 4); + NoteExpressionType (NoteExpressionTypeID typeId, const TChar* title, const TChar* shortTitle, const TChar* units, + int32 unitId, Parameter* associatedParameter, int32 flags = 0); + + /** get the underlying NoteExpressionTypeInfo struct */ + NoteExpressionTypeInfo& getInfo () { return info; } + + /** convert a note expression value to a readable string */ + virtual tresult getStringByValue (NoteExpressionValue valueNormalized /*in*/, String128 string /*out*/); + /** convert a readable string to a note expression value */ + virtual tresult getValueByString (const TChar* string /*in*/, NoteExpressionValue& valueNormalized /*out*/); + + /** gets the current precision (used for string representation of float) */ + int32 getPrecision () const { return precision;} + /** Sets the precision for string representation of float value (for example 4.34 with 2 as precision) */ + void setPrecision (int32 val) { precision = val;} +//----------------------------------------------------------------------------- + OBJ_METHODS(NoteExpressionType, FObject) +protected: + NoteExpressionTypeInfo info; + IPtr associatedParameter; + int32 precision; +}; + +//------------------------------------------------------------------------ +/** Note expression type object representing a custom range. +\ingroup vstClasses */ +//----------------------------------------------------------------------------- +class RangeNoteExpressionType : public NoteExpressionType +{ +public: + RangeNoteExpressionType (NoteExpressionTypeID typeId, const TChar* title, const TChar* shortTitle, const TChar* units, + int32 unitId, NoteExpressionValue defaultPlainValue, NoteExpressionValue plainMin, NoteExpressionValue plainMax, + int32 flags = 0, int32 precision = 4); + + /** Gets the minimum plain value */ + virtual ParamValue getMin () const {return plainMin;} + /** Sets the minimum plain value */ + virtual void setMin (ParamValue value) {plainMin = value;} + /** Gets the maximum plain value */ + virtual ParamValue getMax () const {return plainMax;} + /** Sets the maximum plain value */ + virtual void setMax (ParamValue value) {plainMax = value;} + + virtual tresult getStringByValue (NoteExpressionValue valueNormalized /*in*/, String128 string /*out*/) SMTG_OVERRIDE; + virtual tresult getValueByString (const TChar* string /*in*/, NoteExpressionValue& valueNormalized /*out*/) SMTG_OVERRIDE; +//----------------------------------------------------------------------------- + OBJ_METHODS(RangeNoteExpressionType, NoteExpressionType) +protected: + NoteExpressionValue plainMin; + NoteExpressionValue plainMax; +}; + +//------------------------------------------------------------------------ +/** Collection of note expression types. +\ingroup vstClasses */ +//------------------------------------------------------------------------ +class NoteExpressionTypeContainer : public FObject +{ +public: + /** default constructor */ + NoteExpressionTypeContainer (); + + /** add a note expression type. The container owns the type. No need to release it afterwards. */ + bool addNoteExpressionType (NoteExpressionType* noteExpType); + /** remove a note expression type */ + bool removeNoteExpressionType (NoteExpressionTypeID typeId); + /** remove all note expression types */ + void removeAll (); + + /** get a note expression type object by ID */ + NoteExpressionType* getNoteExpressionType (NoteExpressionTypeID typeId); + + /** get the number of note expression types */ + int32 getNoteExpressionCount (); + + /** get note expression info */ + tresult getNoteExpressionInfo (int32 noteExpressionIndex, NoteExpressionTypeInfo& info /*out*/); + /** convert a note expression value to a readable string */ + tresult getNoteExpressionStringByValue (NoteExpressionTypeID id, NoteExpressionValue valueNormalized /*in*/, String128 string /*out*/); + /** convert a string to a note expression value */ + tresult getNoteExpressionValueByString (NoteExpressionTypeID id, const TChar* string /*in*/, NoteExpressionValue& valueNormalized /*out*/); +//----------------------------------------------------------------------------- + OBJ_METHODS(NoteExpressionTypeContainer, FObject) +protected: + typedef std::vector > NoteExprTypeVector; + NoteExprTypeVector::const_iterator find (NoteExpressionTypeID typeId) const; + + NoteExprTypeVector noteExps; +}; + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vstparameters.cpp b/source/includes/vst3sdk/public.sdk/source/vst/vstparameters.cpp new file mode 100644 index 000000000..a8fa5e52c --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vstparameters.cpp @@ -0,0 +1,445 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vstparameters.cpp +// Created by : Steinberg, 03/2008 +// Description : VST Parameter Implementation +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "vstparameters.h" +#include "pluginterfaces/base/futils.h" +#include "pluginterfaces/base/ustring.h" +#include + +namespace Steinberg { +namespace Vst { + +//------------------------------------------------------------------------ +// Parameter Implementation +//------------------------------------------------------------------------ +Parameter::Parameter () +: valueNormalized (0.) +, precision (4) +{ + memset (&info, 0, sizeof (ParameterInfo)); +} + +//------------------------------------------------------------------------ +Parameter::Parameter (const ParameterInfo& info) +: info (info) +, valueNormalized (info.defaultNormalizedValue) +, precision (4) +{} + +//------------------------------------------------------------------------ +Parameter::Parameter (const TChar* title, ParamID tag, const TChar* units, ParamValue defaultValueNormalized, int32 stepCount, int32 flags, UnitID unitID) +: precision (4) +{ + memset (&info, 0, sizeof (ParameterInfo)); + + UString (info.title, str16BufferSize (String128)).assign (title); + if (units) + UString (info.units, str16BufferSize (String128)).assign (units); + + info.stepCount = stepCount; + info.defaultNormalizedValue = valueNormalized = defaultValueNormalized; + info.flags = flags; + info.id = tag; + info.unitId = unitID; +} + +//------------------------------------------------------------------------ +Parameter::~Parameter () +{} + +//------------------------------------------------------------------------ +bool Parameter::setNormalized (ParamValue normValue) +{ + if (normValue > 1.0) + { + normValue = 1.0; + } + else if (normValue < 0.) + { + normValue = 0.; + } + + if (normValue != valueNormalized) + { + valueNormalized = normValue; + changed (); + return true; + } + return false; +} + +//------------------------------------------------------------------------ +void Parameter::toString (ParamValue normValue, String128 string) const +{ + UString wrapper (string, str16BufferSize (String128)); + if (info.stepCount == 1) + { + if (normValue > 0.5) + { + wrapper.assign (STR16 ("On")); + } + else + { + wrapper.assign (STR16 ("Off")); + } + } + else + { + if (!wrapper.printFloat (normValue, precision)) + string[0] = 0; + } +} + +//------------------------------------------------------------------------ +bool Parameter::fromString (const TChar* string, ParamValue& normValue) const +{ + UString wrapper (const_cast (string), tstrlen (string)); + return wrapper.scanFloat (normValue); +} + +//------------------------------------------------------------------------ +ParamValue Parameter::toPlain (ParamValue normValue) const +{ + return normValue; +} + +//------------------------------------------------------------------------ +ParamValue Parameter::toNormalized (ParamValue plainValue) const +{ + return plainValue; +} + + +//------------------------------------------------------------------------ +// RangeParameter Implementation +//------------------------------------------------------------------------ +RangeParameter::RangeParameter () +: minPlain (0) +, maxPlain (1) +{ +} + +//------------------------------------------------------------------------ +RangeParameter::RangeParameter (const ParameterInfo& paramInfo, ParamValue min, ParamValue max) +: Parameter (paramInfo) +, minPlain (min) +, maxPlain (max) +{ +} + +//------------------------------------------------------------------------ +RangeParameter::RangeParameter (const TChar* title, ParamID tag, const TChar* units, + ParamValue minPlain, ParamValue maxPlain, ParamValue defaultValuePlain, + int32 stepCount, int32 flags, UnitID unitID) +: minPlain (minPlain) +, maxPlain (maxPlain) +{ + UString (info.title, str16BufferSize (String128)).assign (title); + + UString uUnits (info.units, str16BufferSize (String128)); + if (units) + { + uUnits.assign (units); + } + + info.stepCount = stepCount; + info.defaultNormalizedValue = valueNormalized = toNormalized (defaultValuePlain); + info.flags = flags; + info.id = tag; + info.unitId = unitID; +} + +//------------------------------------------------------------------------ +void RangeParameter::toString (ParamValue _valueNormalized, String128 string) const +{ + if (info.stepCount > 1) + { + UString wrapper (string, str16BufferSize (String128)); + int64 plain = static_cast (toPlain (_valueNormalized)); + if (!wrapper.printInt (plain)) + string[0] = 0; + } + else + { + Parameter::toString (toPlain (_valueNormalized), string); + } +} + +//------------------------------------------------------------------------ +bool RangeParameter::fromString (const TChar* string, ParamValue& _valueNormalized) const +{ + UString wrapper (const_cast (string), tstrlen (string)); + if (info.stepCount > 1) + { + int64 plainValue; + if (wrapper.scanInt (plainValue)) + { + _valueNormalized = toNormalized ((ParamValue)plainValue); + return true; + } + return false; + } + if (wrapper.scanFloat (_valueNormalized)) + { + if (_valueNormalized < getMin ()) + _valueNormalized = getMin (); + else if (_valueNormalized > getMax ()) + _valueNormalized = getMax (); + _valueNormalized = toNormalized (_valueNormalized); + return true; + } + return false; +} + +//------------------------------------------------------------------------ +ParamValue RangeParameter::toPlain (ParamValue _valueNormalized) const +{ + if (info.stepCount > 1) + return Min (info.stepCount, (int32)(_valueNormalized * (info.stepCount + 1))) + getMin (); + return _valueNormalized * (getMax () - getMin ()) + getMin (); +} + +//------------------------------------------------------------------------ +ParamValue RangeParameter::toNormalized (ParamValue plainValue) const +{ + if (info.stepCount > 1) + return (plainValue - getMin ()) / info.stepCount; + return (plainValue - getMin ()) / (getMax () - getMin ()); +} + + +//------------------------------------------------------------------------ +// StringListParameter Implementation +//------------------------------------------------------------------------ +StringListParameter::StringListParameter (const ParameterInfo& paramInfo) +: Parameter (paramInfo) +{ +} + +//------------------------------------------------------------------------ +StringListParameter::StringListParameter (const TChar* title, ParamID tag, const TChar* units, + int32 flags, UnitID unitID) +{ + UString (info.title, str16BufferSize (String128)).assign (title); + + UString uUnits (info.units, str16BufferSize (String128)); + if (units) + { + uUnits.assign (units); + } + + info.stepCount = -1; + info.defaultNormalizedValue = 0; + info.flags = flags; + info.id = tag; + info.unitId = unitID; +} + +//------------------------------------------------------------------------ +StringListParameter::~StringListParameter () +{ + for (StringVector::iterator it = strings.begin (), end = strings.end (); it != end; ++it) + std::free (*it); +} + +//------------------------------------------------------------------------ +void StringListParameter::appendString (const String128 string) +{ + int32 length = strlen16 (string); + TChar* buffer = (TChar*)std::malloc ((length + 1) * sizeof (TChar)); + if (!buffer) + return; + memcpy (buffer, string, length * sizeof (TChar)); + buffer[length] = 0; + strings.push_back (buffer); + info.stepCount++; +} + +//------------------------------------------------------------------------ +bool StringListParameter::replaceString (int32 index, const String128 string) +{ + TChar* str = strings.at (index); + if (!str) + return false; + + int32 length = strlen16 (string); + TChar* buffer = (TChar*)malloc ((length + 1) * sizeof (TChar)); + if (!buffer) + return false; + + memcpy (buffer, string, length * sizeof (TChar)); + buffer[length] = 0; + strings[index] = buffer; + std::free (str); + return true; +} + +//------------------------------------------------------------------------ +void StringListParameter::toString (ParamValue _valueNormalized, String128 string) const +{ + int32 index = (int32)toPlain (_valueNormalized); + const TChar* valueString = strings.at (index); + if (valueString) + { + UString (string, str16BufferSize (String128)).assign (valueString); + } + else + string[0] = 0; +} + +//------------------------------------------------------------------------ +bool StringListParameter::fromString (const TChar* string, ParamValue& _valueNormalized) const +{ + int32 index = 0; + for (StringVector::const_iterator it = strings.begin (), end = strings.end (); it != end; ++it, ++index) + { + if (strcmp16 (*it, string) == 0) + { + _valueNormalized = toNormalized ((ParamValue)index); + return true; + } + } + return false; +} + +//------------------------------------------------------------------------ +ParamValue StringListParameter::toPlain (ParamValue _valueNormalized) const +{ + if (info.stepCount <= 0) + return 0; + return Min (info.stepCount, (int32)(_valueNormalized * (info.stepCount + 1))); +} + +//------------------------------------------------------------------------ +ParamValue StringListParameter::toNormalized (ParamValue plainValue) const +{ + if (info.stepCount <= 0) + return 0; + return plainValue / (ParamValue)info.stepCount; +} + +//------------------------------------------------------------------------ +// ParameterContainer Implementation +//------------------------------------------------------------------------ +ParameterContainer::ParameterContainer () +: params (0) +{ +} + +//------------------------------------------------------------------------ +ParameterContainer::~ParameterContainer () +{ + if (params) + delete params; +} + +//------------------------------------------------------------------------ +void ParameterContainer::init (int32 initialSize, int32 resizeDelta) +{ + if (!params) + { + params = new ParameterPtrVector; + if (initialSize > 0) + params->reserve (initialSize); + } +} + +//------------------------------------------------------------------------ +Parameter* ParameterContainer::addParameter (Parameter* p) +{ + if (!params) + init (); + id2index[p->getInfo ().id] = params->size (); + params->push_back (IPtr (p, false)); + return p; +} + +//------------------------------------------------------------------------ +Parameter* ParameterContainer::addParameter (const ParameterInfo& info) +{ + if (!params) + init (); + Parameter* p = new Parameter (info); + if (addParameter (p)) + return p; + p->release (); + return 0; +} + +//------------------------------------------------------------------------ +Parameter* ParameterContainer::getParameter (ParamID tag) +{ + if (params) + { + IndexMap::const_iterator it = id2index.find (tag); + if (it != id2index.end ()) + return params->at (it->second); + } + return 0; +} + +//------------------------------------------------------------------------ +Parameter* ParameterContainer::addParameter (const TChar* title, const TChar* units, + int32 stepCount, ParamValue defaultNormalizedValue, + int32 flags, int32 tag, UnitID unitID) +{ + if (!title) + { + return 0; + } + + ParameterInfo info = {0}; + + UString (info.title, str16BufferSize (String128)).assign (title); + + UString uUnits (info.units, str16BufferSize (String128)); + if (units) + { + uUnits.assign (units); + } + + info.stepCount = stepCount; + info.defaultNormalizedValue = defaultNormalizedValue; + info.flags = flags; + info.id = (tag >= 0) ? tag : getParameterCount (); + info.unitId = unitID; + + return addParameter (info); +} + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vstparameters.h b/source/includes/vst3sdk/public.sdk/source/vst/vstparameters.h new file mode 100644 index 000000000..9c2da056d --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vstparameters.h @@ -0,0 +1,231 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vstparameters.h +// Created by : Steinberg, 03/2008 +// Description : VST Parameter Implementation +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "base/source/fobject.h" +#include "pluginterfaces/vst/ivsteditcontroller.h" +#include "pluginterfaces/vst/ivstunits.h" + +#include +#include + +//------------------------------------------------------------------------ +namespace Steinberg { +namespace Vst { + + +//------------------------------------------------------------------------ +/** Description of a Parameter. +\ingroup vstClasses */ +//------------------------------------------------------------------------ +class Parameter: public FObject +{ +public: +//------------------------------------------------------------------------ + Parameter (); + Parameter (const ParameterInfo&); + Parameter (const TChar* title, ParamID tag, const TChar* units = 0, + ParamValue defaultValueNormalized = 0., int32 stepCount = 0, + int32 flags = ParameterInfo::kCanAutomate, UnitID unitID = kRootUnitId); + virtual ~Parameter (); + + /** Returns its readonly info. */ + const ParameterInfo& getInfo () const {return info;} + + /** Returns its writeable info. */ + ParameterInfo& getInfo () {return info;} + + /** Sets its associated UnitId. */ + void setUnitID (UnitID id) {info.unitId = id;} + /** Gets its associated UnitId. */ + UnitID getUnitID () {return info.unitId;} + + /** Gets its normalized value [0.0, 1.0]. */ + ParamValue getNormalized () const { return valueNormalized; } + /** Sets its normalized value [0.0, 1.0]. */ + virtual bool setNormalized (ParamValue v); + + /** Converts a normalized value to a string. */ + virtual void toString (ParamValue valueNormalized, String128 string) const; + /** Converts a string to a normalized value. */ + virtual bool fromString (const TChar* string, ParamValue& valueNormalized) const; + + /** Converts a normalized value to plain value (e.g. 0.5 to 10000.0Hz). */ + virtual ParamValue toPlain (ParamValue valueNormalized) const; + /** Converts a plain value to a normalized value (e.g. 10000 to 0.5). */ + virtual ParamValue toNormalized (ParamValue plainValue) const; + + /** Gets the current precision (used for string representation of float). */ + int32 getPrecision () const { return precision;} + /** Sets the precision for string representation of float value (for example 4.34 with 2 as precision). */ + void setPrecision (int32 val) { precision = val;} + + OBJ_METHODS (Parameter, FObject) +//------------------------------------------------------------------------ +protected: + ParameterInfo info; + ParamValue valueNormalized; + int32 precision; +}; + +//------------------------------------------------------------------------ +/** Description of a RangeParameter. +\ingroup vstClasses */ +//------------------------------------------------------------------------ +class RangeParameter : public Parameter +{ +public: +//------------------------------------------------------------------------ + RangeParameter (const ParameterInfo& paramInfo, ParamValue min, ParamValue max); + RangeParameter (const TChar* title, ParamID tag, const TChar* units = 0, + ParamValue minPlain = 0., ParamValue maxPlain = 1., ParamValue defaultValuePlain = 0., + int32 stepCount = 0, int32 flags = ParameterInfo::kCanAutomate, UnitID unitID = kRootUnitId); + + /** Gets the minimum plain value, same as toPlain (0). */ + virtual ParamValue getMin () const {return minPlain;} + /** Sets the minimum plain value. */ + virtual void setMin (ParamValue value) {minPlain = value;} + /** Gets the maximum plain value, same as toPlain (1). */ + virtual ParamValue getMax () const {return maxPlain;} + /** Sets the maximum plain value. */ + virtual void setMax (ParamValue value) {maxPlain = value;} + + /** Converts a normalized value to a string. */ + virtual void toString (ParamValue _valueNormalized, String128 string) const SMTG_OVERRIDE; + /** Converts a string to a normalized value. */ + virtual bool fromString (const TChar* string, ParamValue& _valueNormalized) const SMTG_OVERRIDE; + + /** Converts a normalized value to plain value (e.g. 0.5 to 10000.0Hz). */ + virtual ParamValue toPlain (ParamValue _valueNormalized) const SMTG_OVERRIDE; + /** Converts a plain value to a normalized value (e.g. 10000 to 0.5). */ + virtual ParamValue toNormalized (ParamValue plainValue) const SMTG_OVERRIDE; + + OBJ_METHODS (RangeParameter, Parameter) +//------------------------------------------------------------------------ +protected: + RangeParameter (); + + ParamValue minPlain; + ParamValue maxPlain; +}; + +//------------------------------------------------------------------------ +/** Description of a StringListParameter. +\ingroup vstClasses */ +//------------------------------------------------------------------------ +class StringListParameter : public Parameter +{ +public: +//------------------------------------------------------------------------ + StringListParameter (const ParameterInfo& paramInfo); + StringListParameter (const TChar* title, ParamID tag, const TChar* units = 0, + int32 flags = ParameterInfo::kCanAutomate | ParameterInfo::kIsList, UnitID unitID = kRootUnitId); + virtual ~StringListParameter (); + + /** Appends a string and increases the stepCount. */ + virtual void appendString (const String128 string); + /** Replaces the string at index. Index must be between 0 and stepCount+1 */ + virtual bool replaceString (int32 index, const String128 string); + + /** Converts a normalized value to a string. */ + virtual void toString (ParamValue _valueNormalized, String128 string) const SMTG_OVERRIDE; + /** Converts a string to a normalized value. */ + virtual bool fromString (const TChar* string, ParamValue& _valueNormalized) const SMTG_OVERRIDE; + + /** Converts a normalized value to plain value (e.g. 0.5 to 10000.0Hz). */ + virtual ParamValue toPlain (ParamValue _valueNormalized) const SMTG_OVERRIDE; + /** Converts a plain value to a normalized value (e.g. 10000 to 0.5). */ + virtual ParamValue toNormalized (ParamValue plainValue) const SMTG_OVERRIDE; + + OBJ_METHODS (StringListParameter, Parameter) +//------------------------------------------------------------------------ +protected: + typedef std::vector StringVector; + StringVector strings; +}; + +//------------------------------------------------------------------------ +/** Collection of parameters. +\ingroup vstClasses */ +//------------------------------------------------------------------------ +class ParameterContainer +{ +public: +//------------------------------------------------------------------------ + ParameterContainer (); + ~ParameterContainer (); + + /** Init param array. */ + void init (int32 initialSize = 10, int32 resizeDelta = 100); + + /** Creates and adds a new parameter from a ParameterInfo. */ + Parameter* addParameter (const ParameterInfo& info); + + /** Creates and adds a new parameter with given properties. */ + Parameter* addParameter (const TChar* title, const TChar* units = 0, + int32 stepCount = 0, + ParamValue defaultValueNormalized = 0., + int32 flags = ParameterInfo::kCanAutomate, + int32 tag = -1, + UnitID unitID = kRootUnitId); + + /** Adds a given parameter. */ + Parameter* addParameter (Parameter* p); + + /** Returns the count of parameters. */ + int32 getParameterCount () const { return params ? static_cast (params->size ()) : 0; } + + /** Gets parameter by index. */ + Parameter* getParameterByIndex (int32 index) { return params ? params->at (index) : 0; } + + /** Removes all parameters. */ + void removeAll () { if (params) params->clear (); id2index.clear (); } + + /** Gets parameter by ID. */ + Parameter* getParameter (ParamID tag); + +//------------------------------------------------------------------------ +protected: + typedef std::vector > ParameterPtrVector; + typedef std::map IndexMap; + ParameterPtrVector* params; + IndexMap id2index; +}; + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vstpresetfile.cpp b/source/includes/vst3sdk/public.sdk/source/vst/vstpresetfile.cpp new file mode 100644 index 000000000..d80efc721 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vstpresetfile.cpp @@ -0,0 +1,918 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vstpresetfile.cpp +// Created by : Steinberg, 03/2006 +// Description : VST 3 Preset File Format +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "vstpresetfile.h" + +#include + +namespace Steinberg { +namespace Vst { + +//------------------------------------------------------------------------ +// Preset Chunk IDs +//------------------------------------------------------------------------ +static const ChunkID commonChunks[kNumPresetChunks] = +{ + {'V', 'S', 'T', '3'}, // kHeader + {'C', 'o', 'm', 'p'}, // kComponentState + {'C', 'o', 'n', 't'}, // kControllerState + {'P', 'r', 'o', 'g'}, // kProgramData + {'I', 'n', 'f', 'o'}, // kMetaInfo + {'L', 'i', 's', 't'} // kChunkList +}; + +//------------------------------------------------------------------------ +// Preset Header: header id + version + class id + list offset +static const int32 kFormatVersion = 1; +static const int32 kClassIDSize = 32; // ASCII-encoded FUID +static const int32 kHeaderSize = sizeof (ChunkID) + sizeof (int32) + kClassIDSize + sizeof (TSize); +static const int32 kListOffsetPos = kHeaderSize - sizeof (TSize); + +//------------------------------------------------------------------------ +const ChunkID& getChunkID (ChunkType type) +{ + return commonChunks[type]; +} + +#ifdef verify +#undef verify +#endif + +//------------------------------------------------------------------------ +inline bool verify (tresult result) +{ + return result == kResultOk || result == kNotImplemented; +} + +//------------------------------------------------------------------------ +// PresetFile +//------------------------------------------------------------------------ +bool PresetFile::savePreset (IBStream* stream, const FUID& classID, + IComponent* component, IEditController* editController, + const char* xmlBuffer, int32 xmlSize) +{ + PresetFile pf (stream); + pf.setClassID (classID); + if (!pf.writeHeader ()) + return false; + + if (!pf.storeComponentState (component)) + return false; + + if (editController && !pf.storeControllerState (editController)) + return false; + + if (xmlBuffer && !pf.writeMetaInfo (xmlBuffer, xmlSize)) + return false; + + return pf.writeChunkList (); +} + +//------------------------------------------------------------------------ +bool PresetFile::loadPreset (IBStream* stream, const FUID& classID, IComponent* component, + IEditController* editController, std::vector* otherClassIDArray) +{ + PresetFile pf (stream); + if (!pf.readChunkList ()) + return false; + + if (pf.getClassID () != classID) + { + if (otherClassIDArray) + { + // continue to load only if found in supported ID else abort load + if (std::find (otherClassIDArray->begin (), otherClassIDArray->end (), + pf.getClassID ()) == otherClassIDArray->end ()) + return false; + } + else + return false; + } + + if (!pf.restoreComponentState (component)) + return false; + + if (editController) + { + // assign component state to controller + if (!pf.restoreComponentState (editController)) + return false; + + // restore controller-only state (if present) + if (pf.contains (kControllerState) && !pf.restoreControllerState (editController)) + return false; + } + return true; +} + +//------------------------------------------------------------------------ +PresetFile::PresetFile (IBStream* stream) +: stream (stream) +, entryCount (0) +{ + memset (entries, 0, sizeof (entries)); + + if (stream) + stream->addRef (); +} + +//------------------------------------------------------------------------ +PresetFile::~PresetFile () +{ + if (stream) + stream->release (); +} + +//------------------------------------------------------------------------ +const PresetFile::Entry* PresetFile::getEntry (ChunkType which) const +{ + const ChunkID& id = getChunkID (which); + for (int32 i = 0; i < entryCount; i++) + if (isEqualID (entries[i].id, id)) + return &entries[i]; + return 0; +} + +//------------------------------------------------------------------------ +const PresetFile::Entry* PresetFile::getLastEntry () const +{ + return entryCount > 0 ? &entries[entryCount - 1] : 0; +} + +//------------------------------------------------------------------------ +bool PresetFile::readID (ChunkID id) +{ + int32 numBytesRead = 0; + stream->read (id, sizeof (ChunkID), &numBytesRead); + return numBytesRead == sizeof (ChunkID); +} + +//------------------------------------------------------------------------ +bool PresetFile::writeID (const ChunkID id) +{ + int32 numBytesWritten = 0; + stream->write ((void*)id, sizeof (ChunkID), &numBytesWritten); + return numBytesWritten == sizeof (ChunkID); +} + +//------------------------------------------------------------------------ +bool PresetFile::readEqualID (const ChunkID id) +{ + ChunkID temp = {0}; + return readID (temp) && isEqualID (temp, id); +} + +//------------------------------------------------------------------------ +bool PresetFile::readSize (TSize& size) +{ + int32 numBytesRead = 0; + stream->read (&size, sizeof (TSize), &numBytesRead); +#if BYTEORDER == kBigEndian + SWAP_64 (size) +#endif + return numBytesRead == sizeof (TSize); +} + +//------------------------------------------------------------------------ +bool PresetFile::writeSize (TSize size) +{ +#if BYTEORDER == kBigEndian + SWAP_64 (size) +#endif + int32 numBytesWritten = 0; + stream->write (&size, sizeof (TSize), &numBytesWritten); + return numBytesWritten == sizeof (TSize); +} + +//------------------------------------------------------------------------ +bool PresetFile::readInt32 (int32& value) +{ + int32 numBytesRead = 0; + stream->read (&value, sizeof (int32), &numBytesRead); +#if BYTEORDER == kBigEndian + SWAP_32 (value) +#endif + return numBytesRead == sizeof (int32); +} + +//------------------------------------------------------------------------ +bool PresetFile::writeInt32 (int32 value) +{ +#if BYTEORDER == kBigEndian + SWAP_32 (value) +#endif + int32 numBytesWritten = 0; + stream->write (&value, sizeof (int32), &numBytesWritten); + return numBytesWritten == sizeof (int32); +} + +//------------------------------------------------------------------------ +bool PresetFile::seekTo (TSize offset) +{ + int64 result = -1; + stream->seek (offset, IBStream::kIBSeekSet, &result); + return result == offset; +} + +//------------------------------------------------------------------------ +bool PresetFile::readChunkList () +{ + seekTo (0); + entryCount = 0; + + char8 classString[kClassIDSize + 1] = {0}; + + // Read header + int32 version = 0; + TSize listOffset = 0; + if (!(readEqualID (getChunkID (kHeader)) && + readInt32 (version) && + verify (stream->read (classString, kClassIDSize)) && + readSize (listOffset) && + listOffset > 0 && + seekTo (listOffset))) + return false; + + classID.fromString (classString); + + // Read list + int32 count = 0; + if (!readEqualID (getChunkID (kChunkList))) + return false; + if (!readInt32 (count)) + return false; + + if (count > kMaxEntries) + count = kMaxEntries; + + for (int32 i = 0; i < count; i++) + { + Entry& e = entries[i]; + if (!(readID (e.id) && + readSize (e.offset) && + readSize (e.size))) + break; + + entryCount++; + } + + return entryCount > 0; +} + +//------------------------------------------------------------------------ +bool PresetFile::writeHeader () +{ + // header id + version + class id + list offset (unknown yet) + + char8 classString[kClassIDSize + 1] = {0}; + classID.toString (classString); + + return seekTo (0) && + writeID (getChunkID (kHeader)) && + writeInt32 (kFormatVersion) && + verify (stream->write (classString, kClassIDSize)) && + writeSize (0); +} + +//------------------------------------------------------------------------ +bool PresetFile::writeChunkList () +{ + // Update list offset + TSize pos = 0; + stream->tell (&pos); + if (!(seekTo (kListOffsetPos) && + writeSize (pos) && + seekTo (pos))) + return false; + + // Write list + if (!writeID (getChunkID (kChunkList))) + return false; + if (!writeInt32 (entryCount)) + return false; + + for (int32 i = 0; i < entryCount; i++) + { + Entry& e = entries[i]; + if (!(writeID (e.id) && + writeSize (e.offset) && + writeSize (e.size))) + return false; + } + return true; +} + +//------------------------------------------------------------------------ +bool PresetFile::beginChunk (Entry& e, ChunkType which) +{ + if (entryCount >= kMaxEntries) + return false; + + const ChunkID& id = getChunkID (which); + memcpy (e.id, &id, sizeof (ChunkID)); + stream->tell (&e.offset); + e.size = 0; + return true; +} + +//------------------------------------------------------------------------ +bool PresetFile::endChunk (Entry& e) +{ + if (entryCount >= kMaxEntries) + return false; + + TSize pos = 0; + stream->tell (&pos); + e.size = pos - e.offset; + entries[entryCount++] = e; + return true; +} + +//------------------------------------------------------------------------ +bool PresetFile::readMetaInfo (char* xmlBuffer, int32& size) +{ + bool result = false; + const Entry* e = getEntry (kMetaInfo); + if (e) + { + if (xmlBuffer) + { + result = seekTo (e->offset) && + verify (stream->read (xmlBuffer, size, &size)); + } + else + { + size = (int32)e->size; + result = size > 0; + } + } + return result; +} + +//------------------------------------------------------------------------ +bool PresetFile::writeMetaInfo (const char* xmlBuffer, int32 size, bool forceWriting) +{ + if (contains (kMetaInfo)) // already exists! + { + if (!forceWriting) + return false; + } + if (!prepareMetaInfoUpdate ()) + return false; + + if (size == -1) + size = (int32)strlen (xmlBuffer); + + Entry e = {0}; + return beginChunk (e, kMetaInfo) && + verify (stream->write ((void*)xmlBuffer, size)) && + endChunk (e); +} + +//------------------------------------------------------------------------ +bool PresetFile::prepareMetaInfoUpdate () +{ + TSize writePos = 0; + const Entry* e = getEntry (kMetaInfo); + if (e) + { + // meta info must be the last entry! + if (e != getLastEntry ()) + return false; + + writePos = e->offset; + entryCount--; + } + else + { + // entries must be sorted ascending by offset! + e = getLastEntry (); + writePos = e ? e->offset + e->size : kHeaderSize; + } + + return seekTo (writePos); +} + +//------------------------------------------------------------------------ +bool PresetFile::writeChunk (const void* data, int32 size, ChunkType which) +{ + if (contains (which)) // already exists! + return false; + + Entry e = {0}; + return beginChunk (e, which) && + verify (stream->write ((void*)data, size)) && + endChunk (e); +} + +//------------------------------------------------------------------------ +bool PresetFile::seekToComponentState () +{ + const Entry* e = getEntry (kComponentState); + return e && seekTo (e->offset); +} + +//------------------------------------------------------------------------ +bool PresetFile::storeComponentState (IComponent* component) +{ + if (contains (kComponentState)) // already exists! + return false; + + Entry e = {0}; + return beginChunk (e, kComponentState) && + verify (component->getState (stream)) && + endChunk (e); +} + +//------------------------------------------------------------------------ +bool PresetFile::restoreComponentState (IComponent* component) +{ + const Entry* e = getEntry (kComponentState); + if (e) + { + ReadOnlyBStream* readOnlyBStream = new ReadOnlyBStream (stream, e->offset, e->size); + FReleaser readOnlyBStreamReleaser (readOnlyBStream); + return verify (component->setState (readOnlyBStream)); + } + return false; +} + +//------------------------------------------------------------------------ +bool PresetFile::restoreComponentState (IEditController* editController) +{ + const Entry* e = getEntry (kComponentState); + if (e) + { + ReadOnlyBStream* readOnlyBStream = new ReadOnlyBStream (stream, e->offset, e->size); + FReleaser readOnlyBStreamReleaser (readOnlyBStream); + return verify (editController->setComponentState (readOnlyBStream)); + } + return false; +} + +//------------------------------------------------------------------------ +bool PresetFile::seekToControllerState () +{ + const Entry* e = getEntry (kControllerState); + return e && seekTo (e->offset); +} + +//------------------------------------------------------------------------ +bool PresetFile::storeControllerState (IEditController* editController) +{ + if (contains (kControllerState)) // already exists! + return false; + + Entry e = {0}; + return beginChunk (e, kControllerState) && + verify (editController->getState (stream)) && + endChunk (e); +} + +//------------------------------------------------------------------------ +bool PresetFile::restoreControllerState (IEditController* editController) +{ + const Entry* e = getEntry (kControllerState); + if (e) + { + ReadOnlyBStream* readOnlyBStream = new ReadOnlyBStream (stream, e->offset, e->size); + FReleaser readOnlyBStreamReleaser (readOnlyBStream); + return verify (editController->setState (readOnlyBStream)); + } + return false; +} + +//------------------------------------------------------------------------ +bool PresetFile::storeProgramData (IBStream* inStream, ProgramListID listID) +{ + if (contains (kProgramData)) // already exists! + return false; + + writeHeader (); + + Entry e = {0}; + if (beginChunk (e, kProgramData)) + { + if (writeInt32 (listID)) + { + int8 buffer[8192]; + int32 read = 0; + int32 written = 0; + while (inStream->read (buffer, 8192, &read) == kResultTrue && read > 0) + { + if (stream->write (buffer, read, &written) != kResultTrue) + { + return false; + } + } + return endChunk (e); + } + } + return false; +} + +//------------------------------------------------------------------------ +bool PresetFile::storeProgramData (IProgramListData* programListData, ProgramListID listID, int32 programIndex) +{ + if (contains (kProgramData)) // already exists! + return false; + + writeHeader (); + + Entry e = {0}; + return beginChunk (e, kProgramData) && + writeInt32 (listID) && + verify (programListData->getProgramData (listID, programIndex, stream)) && + endChunk (e); +} + +//------------------------------------------------------------------------ +bool PresetFile::restoreProgramData (IProgramListData* programListData, ProgramListID* programListID, int32 programIndex) +{ + const Entry* e = getEntry (kProgramData); + ProgramListID savedProgramListID = -1; + if (e && seekTo (e->offset)) + { + if (readInt32 (savedProgramListID)) + { + if (programListID && *programListID != savedProgramListID) + return false; + + int32 alreadyRead = sizeof (int32); + ReadOnlyBStream* readOnlyBStream = new ReadOnlyBStream (stream, e->offset + alreadyRead, e->size - alreadyRead); + FReleaser readOnlyBStreamReleaser (readOnlyBStream); + return programListData && verify (programListData->setProgramData (savedProgramListID, programIndex, readOnlyBStream)); + } + } + return false; +} + +//------------------------------------------------------------------------ +bool PresetFile::storeProgramData (IUnitData* unitData, UnitID unitID) +{ + if (contains (kProgramData)) // already exists! + return false; + + writeHeader (); + + Entry e = {0}; + return beginChunk (e, kProgramData) && + writeInt32 (unitID) && + verify (unitData->getUnitData (unitID, stream)) && + endChunk (e); +} + +//------------------------------------------------------------------------ +bool PresetFile::restoreProgramData (IUnitData* unitData, UnitID* unitId) +{ + const Entry* e = getEntry (kProgramData); + UnitID savedUnitID = -1; + if (e && seekTo (e->offset)) + { + if (readInt32 (savedUnitID)) + { + if (unitId && *unitId != savedUnitID) + return false; + + int32 alreadyRead = sizeof (int32); + ReadOnlyBStream* readOnlyBStream = new ReadOnlyBStream (stream, e->offset + alreadyRead, e->size - alreadyRead); + FReleaser readOnlyStreamReleaser (readOnlyBStream); + return (unitData && verify (unitData->setUnitData (savedUnitID, readOnlyBStream))); + } + } + return false; +} + +//------------------------------------------------------------------------ +bool PresetFile::restoreProgramData (IUnitInfo* unitInfo, int32 unitProgramListID, int32 programIndex) +{ + const Entry* e = getEntry (kProgramData); + int32 savedProgramListID = -1; + if (e && seekTo (e->offset)) + { + if (readInt32 (savedProgramListID)) + { + if (unitProgramListID != savedProgramListID) + return false; + + int32 alreadyRead = sizeof (int32); + ReadOnlyBStream* readOnlyBStream = new ReadOnlyBStream (stream, e->offset + alreadyRead, e->size - alreadyRead); + FReleaser readOnlyStreamReleaser (readOnlyBStream); + return (unitInfo && unitInfo->setUnitProgramData (unitProgramListID, programIndex, readOnlyBStream)); + } + } + return false; +} + +//------------------------------------------------------------------------ +bool PresetFile::getUnitProgramListID (int32& unitProgramListID) +{ + const Entry* e = getEntry (kProgramData); + if (e && seekTo (e->offset)) + { + if (readInt32 (unitProgramListID)) + { + return true; + } + } + return false; +} + + +//------------------------------------------------------------------------ +// FileStream implementation +//------------------------------------------------------------------------ +IBStream* FileStream::open (const char* filename, const char* mode) +{ + FILE* file = fopen (filename, mode); + return file ? new FileStream (file) : 0; +} + +//------------------------------------------------------------------------ +FileStream::FileStream (FILE* file) +: file (file) +{ + FUNKNOWN_CTOR +} + +//------------------------------------------------------------------------ +FileStream::~FileStream () +{ + fclose (file); + FUNKNOWN_DTOR +} + +//------------------------------------------------------------------------ +IMPLEMENT_FUNKNOWN_METHODS (FileStream, IBStream, IBStream::iid) + +//------------------------------------------------------------------------ +tresult PLUGIN_API FileStream::read (void* buffer, int32 numBytes, int32* numBytesRead) +{ + size_t result = fread (buffer, 1, numBytes, file); + if (numBytesRead) + *numBytesRead = (int32)result; + return result == numBytes ? kResultOk : kResultFalse; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API FileStream::write (void* buffer, int32 numBytes, int32* numBytesWritten) +{ + size_t result = fwrite (buffer, 1, numBytes, file); + if (numBytesWritten) + *numBytesWritten = (int32)result; + return result == numBytes ? kResultOk : kResultFalse; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API FileStream::seek (int64 pos, int32 mode, int64* result) +{ + if (fseek (file, (int32)pos, mode) == 0) + { + if (result) + *result = ftell (file); + return kResultOk; + } + return kResultFalse; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API FileStream::tell (int64* pos) +{ + if (pos) + *pos = ftell (file); + return kResultOk; +} + + +//------------------------------------------------------------------------ +// ReadOnlyBStream implementation +//------------------------------------------------------------------------ + +IMPLEMENT_REFCOUNT (ReadOnlyBStream) + +//------------------------------------------------------------------------ +ReadOnlyBStream::ReadOnlyBStream (IBStream* sourceStream, TSize sourceOffset, TSize sectionSize) +: sourceStream (sourceStream) +, sourceOffset (sourceOffset) +, sectionSize (sectionSize) +, seekPosition (0) +{ + FUNKNOWN_CTOR + if (sourceStream) + sourceStream->addRef (); +} + +//------------------------------------------------------------------------ +ReadOnlyBStream::~ReadOnlyBStream () +{ + if (sourceStream) + sourceStream->release (); + + FUNKNOWN_DTOR +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API ReadOnlyBStream::queryInterface (const TUID iid, void** obj) +{ + return sourceStream ? sourceStream->queryInterface (iid, obj) : kResultFalse; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API ReadOnlyBStream::read (void* buffer, int32 numBytes, int32* numBytesRead) +{ + if (numBytesRead) + *numBytesRead = 0; + + if (!sourceStream) + return kNotInitialized; + + int32 maxBytesToRead = (int32)(sectionSize - seekPosition); + if (numBytes > maxBytesToRead) + numBytes = maxBytesToRead; + if (numBytes <= 0) + return kResultOk; + + tresult result = sourceStream->seek (sourceOffset + seekPosition, kIBSeekSet); + if (result != kResultOk) + return result; + + int32 numRead = 0; + result = sourceStream->read (buffer, numBytes, &numRead); + + if (numRead > 0) + seekPosition += numRead; + if (numBytesRead) + *numBytesRead = numRead; + + return result; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API ReadOnlyBStream::write (void* /*buffer*/, int32 /*numBytes*/, int32* numBytesWritten) +{ + if (numBytesWritten) + *numBytesWritten = 0; + + return kNotImplemented; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API ReadOnlyBStream::seek (int64 pos, int32 mode, int64* result) +{ + switch (mode) + { + case kIBSeekSet : + seekPosition = pos; + break; + + case kIBSeekCur : + seekPosition += pos; + break; + + case kIBSeekEnd : + seekPosition = sectionSize + pos; + break; + } + + if (seekPosition < 0) + seekPosition = 0; + if (seekPosition > sectionSize) + seekPosition = sectionSize; + + if (result) + *result = seekPosition; + return kResultOk; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API ReadOnlyBStream::tell (int64* pos) +{ + if (pos) + *pos = seekPosition; + return kResultOk; +} + + +//------------------------------------------------------------------------ +// BufferStream implementation +//------------------------------------------------------------------------ +IMPLEMENT_FUNKNOWN_METHODS (BufferStream, IBStream, IBStream::iid) + +//------------------------------------------------------------------------ +BufferStream::BufferStream () +{ + FUNKNOWN_CTOR +} + +//------------------------------------------------------------------------ +BufferStream::~BufferStream () +{ + FUNKNOWN_DTOR +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API BufferStream::read (void* buffer, int32 numBytes, int32* numBytesRead) +{ + uint32 size = mBuffer.get (buffer, numBytes); + if (numBytesRead) + *numBytesRead = size; + + return kResultTrue; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API BufferStream::write (void* buffer, int32 numBytes, int32* numBytesWritten) +{ + bool res = mBuffer.put (buffer, numBytes); + if (numBytesWritten) + *numBytesWritten = res ? numBytes : 0; + + return res ? kResultTrue : kResultFalse; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API BufferStream::seek (int64 pos, int32 mode, int64* result) +{ + bool res = false; + switch (mode) + { + //--- ----------------- + case IBStream::kIBSeekSet: + { + int64 tmp = pos; + if (tmp < 0) + tmp = 0; + res = mBuffer.setFillSize (static_cast (tmp)); + } + break; + + //--- ----------------- + case IBStream::kIBSeekCur: + { + int64 tmp = mBuffer.getFillSize () + pos; + if (tmp < 0) + tmp = 0; + res = mBuffer.setFillSize (static_cast (tmp)); + } + break; + + //--- ----------------- + case IBStream::kIBSeekEnd: + { + int64 tmp = mBuffer.getSize () - pos; + if (tmp < 0) + tmp = 0; + res = mBuffer.setFillSize (static_cast (tmp)); + } + break; + } + if (res && result) + *result = mBuffer.getFillSize (); + + return res ? kResultTrue : kResultFalse; +} + +//------------------------------------------------------------------------ +tresult PLUGIN_API BufferStream::tell (int64* pos) +{ + if (pos) + *pos = mBuffer.getFillSize (); + return pos ? kResultTrue : kResultFalse; +} + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vstpresetfile.h b/source/includes/vst3sdk/public.sdk/source/vst/vstpresetfile.h new file mode 100644 index 000000000..0b0e9b4d9 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vstpresetfile.h @@ -0,0 +1,306 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vstpresetfile.h +// Created by : Steinberg, 03/2006 +// Description : VST 3 Preset File Format +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "pluginterfaces/vst/ivstcomponent.h" +#include "pluginterfaces/vst/ivsteditcontroller.h" +#include "pluginterfaces/vst/ivstunits.h" + +#include "pluginterfaces/base/ibstream.h" +#include "base/source/fbuffer.h" + +#include +#include + +//------------------------------------------------------------------------ +/** \page vst3presetFileFormat VST 3 Preset File Format Definition +\code + + VST 3 Preset File Format Definition + =================================== + +0 +---------------------------+ + | HEADER | + | header id ('VST3') | 4 Bytes + | version | 4 Bytes (int32) + | ASCII-encoded class id | 32 Bytes + +--| offset to chunk list | 8 Bytes (int64) + | +---------------------------+ + | | DATA AREA |<-+ + | | data of chunks 1..n | | + | ... ... | + | | | | + +->+---------------------------+ | + | CHUNK LIST | | + | list id ('List') | | 4 Bytes + | entry count | | 4 Bytes (int32) + +---------------------------+ | + | 1..n | | + | +----------------------+ | | + | | chunk id | | | 4 Bytes + | | offset to chunk data |----+ 8 Bytes (int64) + | | size of chunk data | | 8 Bytes (int64) + | +----------------------+ | +EOF +---------------------------+ + +\endcode +\see \ref Steinberg::Vst::PresetFile +*/ +//------------------------------------------------------------------------ + +//------------------------------------------------------------------------ +namespace Steinberg { +namespace Vst { + +//------------------------------------------------------------------------ +typedef char ChunkID[4]; + +//------------------------------------------------------------------------ +enum ChunkType +{ + kHeader, + kComponentState, + kControllerState, + kProgramData, + kMetaInfo, + kChunkList, + kNumPresetChunks +}; + +//------------------------------------------------------------------------ +extern const ChunkID& getChunkID (ChunkType type); + +//------------------------------------------------------------------------ +inline bool isEqualID (const ChunkID id1, const ChunkID id2) +{ + return memcmp (id1, id2, sizeof (ChunkID)) == 0; +} + +//------------------------------------------------------------------------ +/** Handler for a VST 3 Preset File. +\ingroup vstClasses +\see \ref vst3presetFileFormat */ +//------------------------------------------------------------------------ +class PresetFile +{ +public: +//------------------------------------------------------------------------ + PresetFile (IBStream* stream); ///< Constructor of Preset file based on a stream + virtual ~PresetFile (); + + /** Internal structure used for chunk handling */ + struct Entry + { + ChunkID id; + TSize offset; + TSize size; + }; + + IBStream* getStream () { return stream; } ///< Returns the associated stream. + + const FUID& getClassID () const { return classID; } ///< Returns the associated classID (component ID: Processor part (not the controller!)). + void setClassID (const FUID& uid) { classID = uid; } ///< Sets the associated classID (component ID: Processor part (not the controller!)). + + const Entry* getEntry (ChunkType which) const; ///< Returns an entry for a given chunk type. + const Entry* getLastEntry () const; ///< Returns the last available entry. + int32 getEntryCount () const { return entryCount; } ///< Returns the number of total entries in the current stream. + const Entry& at (int32 index) const { return entries[index]; } ///< Returns the entry at a given position. + bool contains (ChunkType which) const { return getEntry (which) != 0; } ///< Checks if a given chunk type exist in the stream. + + bool readChunkList (); ///< Reads and build the chunk list (including the header chunk). + bool writeHeader (); ///< Writes into the stream the main header. + bool writeChunkList (); ///< Writes into the stream the chunk list (should be at the end). + + /** Reads the meta XML info and its size, the size could be retrieved by passing zero as xmlBuffer. */ + bool readMetaInfo (char* xmlBuffer, int32& size); + + /** Writes the meta XML info, -1 means null-terminated, forceWriting to true will force to rewrite the XML Info when the chunk already exists. */ + bool writeMetaInfo (const char* xmlBuffer, int32 size = -1, bool forceWriting = false); + bool prepareMetaInfoUpdate (); ///< checks if meta info chunk is the last one and jump to correct position. + + /** Writes a given data of a given size as "which" chunk type. */ + bool writeChunk (const void* data, int32 size, ChunkType which = kComponentState); + + //------------------------------------------------------------- + // for storing and restoring the whole Plug-in state (component and controller states) + bool seekToComponentState (); ///< Seeks to the begin of the Component State. + bool storeComponentState (IComponent* component); ///< Stores the component state (only one time). + bool restoreComponentState (IComponent* component); ///< Restores the component state. + + bool seekToControllerState (); ///< Seeks to the begin of the Controller State. + bool storeControllerState (IEditController* editController); ///< Stores the controller state (only one time). + bool restoreControllerState (IEditController* editController); ///< Restores the controller state. + + bool restoreComponentState (IEditController* editController); ///< Restores the component state and apply it to the controller. + + //------------------------------------------------------------- + /** Store program data or unit data from stream (including the header chunk). + \param listID could be ProgramListID or UnitID. */ + bool storeProgramData (IBStream* inStream, ProgramListID listID); + + //----when Plug-in uses IProgramListData----------------------- + /** Stores a IProgramListData with a given identifier and index (including the header chunk). */ + bool storeProgramData (IProgramListData* programListData, ProgramListID programListID, int32 programIndex); + /** Restores a IProgramListData with a given identifier and index. */ + bool restoreProgramData (IProgramListData* programListData, ProgramListID* programListID = 0, int32 programIndex = 0); + + //----when Plug-in uses IUnitData------------------------------ + /** Stores a IUnitData with a given unitID (including the header chunk). */ + bool storeProgramData (IUnitData* unitData, UnitID unitID); + /** Restores a IUnitData with a given unitID (optional). */ + bool restoreProgramData (IUnitData* unitData, UnitID* unitID = 0); + + //------------------------------------------------------------- + /** for keeping the Controller part in sync concerning preset data stream, unitProgramListID could be ProgramListID or UnitID. */ + bool restoreProgramData (IUnitInfo* unitInfo, int32 unitProgramListID, int32 programIndex = -1); + + /** Gets the unitProgramListID saved in the kProgramData chunk (if available). */ + bool getUnitProgramListID (int32& unitProgramListID); + + //------------------------------------------------------------------------ + /** Shortcut helper to create preset from component/controller state. classID is the FUID of the component (processor) part. */ + static bool savePreset (IBStream* stream, + const FUID& classID, + IComponent* component, + IEditController* editController = 0, + const char* xmlBuffer = 0, + int32 xmlSize = -1); + + /** Shortcut helper to load preset with component/controller state. classID is the FUID of the component (processor) part. */ + static bool loadPreset (IBStream* stream, + const FUID& classID, + IComponent* component, + IEditController* editController = 0, + std::vector* otherClassIDArray = 0); +//------------------------------------------------------------------------ +protected: + bool readID (ChunkID id); + bool writeID (const ChunkID id); + bool readEqualID (const ChunkID id); + bool readSize (TSize& size); + bool writeSize (TSize size); + bool readInt32 (int32& value); + bool writeInt32 (int32 value); + bool seekTo (TSize offset); + bool beginChunk (Entry& e, ChunkType which); + bool endChunk (Entry& e); + + IBStream* stream; + FUID classID; ///< classID is the FUID of the component (processor) part + enum { kMaxEntries = 128 }; + Entry entries[kMaxEntries]; + int32 entryCount; +}; + +//------------------------------------------------------------------------ +/** Stream implementation for a file using stdio. */ +//------------------------------------------------------------------------ +class FileStream: public IBStream +{ +public: +//------------------------------------------------------------------------ + static IBStream* open (const char* filename, const char* mode); ///< open a stream using stdio function + + //---from FUnknown------------------ + DECLARE_FUNKNOWN_METHODS + + //---from IBStream------------------ + tresult PLUGIN_API read (void* buffer, int32 numBytes, int32* numBytesRead = 0) SMTG_OVERRIDE; + tresult PLUGIN_API write (void* buffer, int32 numBytes, int32* numBytesWritten = 0) SMTG_OVERRIDE; + tresult PLUGIN_API seek (int64 pos, int32 mode, int64* result = 0) SMTG_OVERRIDE; + tresult PLUGIN_API tell (int64* pos) SMTG_OVERRIDE; + +//------------------------------------------------------------------------ +protected: + FileStream (FILE* file); + virtual ~FileStream (); + + FILE* file; +}; + +//------------------------------------------------------------------------ +/** Stream representing a Read-Only subsection of its source stream. */ +//------------------------------------------------------------------------ +class ReadOnlyBStream: public IBStream +{ +public: +//------------------------------------------------------------------------ + ReadOnlyBStream (IBStream* sourceStream, TSize sourceOffset, TSize sectionSize); + virtual ~ReadOnlyBStream (); + + //---from FUnknown------------------ + DECLARE_FUNKNOWN_METHODS + + //---from IBStream------------------ + tresult PLUGIN_API read (void* buffer, int32 numBytes, int32* numBytesRead = 0) SMTG_OVERRIDE; + tresult PLUGIN_API write (void* buffer, int32 numBytes, int32* numBytesWritten = 0) SMTG_OVERRIDE; + tresult PLUGIN_API seek (int64 pos, int32 mode, int64* result = 0) SMTG_OVERRIDE; + tresult PLUGIN_API tell (int64* pos) SMTG_OVERRIDE; + +//------------------------------------------------------------------------ +protected: + IBStream* sourceStream; + TSize sourceOffset; + TSize sectionSize; + TSize seekPosition; +}; + +//------------------------------------------------------------------------ +/** Stream implementation for a memory buffer. */ +//------------------------------------------------------------------------ +class BufferStream : public IBStream +{ +public: + BufferStream (); + virtual ~BufferStream (); + + //---from FUnknown------------------ + DECLARE_FUNKNOWN_METHODS + + //---from IBStream------------------ + tresult PLUGIN_API read (void* buffer, int32 numBytes, int32* numBytesRead = 0) SMTG_OVERRIDE; + tresult PLUGIN_API write (void* buffer, int32 numBytes, int32* numBytesWritten = 0) SMTG_OVERRIDE; + tresult PLUGIN_API seek (int64 pos, int32 mode, int64* result = 0) SMTG_OVERRIDE; + tresult PLUGIN_API tell (int64* pos) SMTG_OVERRIDE; + +protected: + Buffer mBuffer; +}; + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vstrepresentation.cpp b/source/includes/vst3sdk/public.sdk/source/vst/vstrepresentation.cpp new file mode 100644 index 000000000..2f1cb0bbd --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vstrepresentation.cpp @@ -0,0 +1,434 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vstrepresentation.cpp +// Created by : Steinberg, 08/2010 +// Description : VST Representation Helper +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "vstrepresentation.h" + +#include "base/source/fstring.h" +#include "pluginterfaces/base/ibstream.h" + +namespace Steinberg { +namespace Vst { + +#define START_TAG_STRING(string) "<" string">" +#define END_TAG_STRING(string) "" +#define MEDIUM_TITLE_LIMIT 8 +#define SHORT_TITLE_LIMIT 4 + +//------------------------------------------------------------------------ +struct StringWriter +{ + StringWriter (IBStream* stream) : stream (stream) {} + + void write (const ConstString& str) + { + stream->write (const_cast (str.text8 ()), str.length (), 0); + } + + IBStream* stream; +}; + +//------------------------------------------------------------------------ +// XmlRepresentationHelper Implementation +//------------------------------------------------------------------------ +XmlRepresentationHelper::XmlRepresentationHelper (const Vst::RepresentationInfo& info, + const FIDString companyName, + const FIDString pluginName, const TUID& pluginUID, + IBStream* stream) +: stream (stream) +{ + StringWriter writer (stream); + String string; + writer.write (""); + writer.write (ENDLINE_A); + string.printf ("", ROOTXML_TAG); + writer.write (string.text8 ()); + writer.write (ENDLINE_A); + + string.printf ("<%s %s=\"1.0\">", ROOTXML_TAG, ATTR_VERSION); + writer.write (string.text8 ()); + writer.write (ENDLINE_A); + + //---Plug-in Tag---------------- + FUID uid (pluginUID); + char uidText[33]; + uid.toString (uidText); + + string.printf ("<%s %s=\"%s\" %s=\"%s\" %s=\"%s\"/>", PLUGIN_TAG, ATTR_CLASSID, uidText, ATTR_NAME, pluginName, ATTR_VENDOR, companyName); + writer.write (string); + writer.write (ENDLINE_A); + + //---Representation Tag---------------- + string.printf ("\t<%s", REPRESENTATION_TAG); + writer.write (string); + string.printf (" %s=\"%s\"", ATTR_NAME, info.name); + writer.write (string); + string.printf (" %s=\"%s\"", ATTR_VENDOR, info.vendor); + writer.write (string); + string.printf (" %s=\"%s\"", ATTR_VERSION, info.version); + writer.write (string); + if (strcmp ((char*)info.host, "")) + { + string.printf (" %s=\"%s\"", ATTR_HOST, info.host); + writer.write (string); + } + writer.write (">"); + writer.write (ENDLINE_A); + + state = kInRepresentation; +} + +//------------------------------------------------------------------------ +XmlRepresentationHelper::~XmlRepresentationHelper () +{ + if (state == kInLayer) + endLayer (); + if (state == kInCell) + endCell (); + if (state == kInPage) + endPage (); + + StringWriter writer (stream); + String string; + + // end representation + string.printf ("\t%s", END_TAG_STRING(REPRESENTATION_TAG)); + writer.write (string); + writer.write (ENDLINE_A); + + // end piper + writer.write (END_TAG_STRING(ROOTXML_TAG)); + writer.write (ENDLINE_A); +} + +//------------------------------------------------------------------------ +bool XmlRepresentationHelper::checkState (int32 newState) +{ + if (newState - state == 1 || state - newState == 1) // we go down or up + { + state = newState; + return true; + } + return false; +} + +//------------------------------------------------------------------------ +bool XmlRepresentationHelper::startPage (FIDString name, int32 unitID) +{ + if (!checkState (kInPage)) + return false; + + StringWriter writer (stream); + String string; + if (unitID != -1) + string.printf ("<%s %s=\"%s\" %s=\"%d\">", PAGE_TAG, ATTR_NAME, name, ATTR_UNITID, unitID); + else + string.printf ("<%s %s=\"%s\">", PAGE_TAG, ATTR_NAME, name); + writer.write (string); + writer.write (ENDLINE_A); + + return true; +} + +//------------------------------------------------------------------------ +bool XmlRepresentationHelper::endPage () +{ + if (!checkState (kInRepresentation)) + return false; + + StringWriter writer (stream); + String string; + string.printf ("%s", END_TAG_STRING(PAGE_TAG)); + writer.write (string); + writer.write (ENDLINE_A); + + return true; +} + +//------------------------------------------------------------------------ +bool XmlRepresentationHelper::startCell () +{ + if (!checkState (kInCell)) + return false; + + StringWriter writer (stream); + String string; + string.printf ("%s", START_TAG_STRING(CELL_TAG)); + writer.write (string); + writer.write (ENDLINE_A); + + return true; +} + +//------------------------------------------------------------------------ +bool XmlRepresentationHelper::endCell () +{ + if (!checkState (kInPage)) + return false; + + StringWriter writer (stream); + String string; + string.printf ("%s", END_TAG_STRING(CELL_TAG)); + writer.write (string); + writer.write (ENDLINE_A); + + return true; +} + +//------------------------------------------------------------------------ +bool XmlRepresentationHelper::startEndCell () +{ + if (!checkState (kInCell)) + return false; + + StringWriter writer (stream); + String string; + string.printf ("<%s/>", CELL_TAG); + writer.write (string); + writer.write (ENDLINE_A); + + if (!checkState (kInPage)) + return false; + return true; +} + +//------------------------------------------------------------------------ +bool XmlRepresentationHelper::startLayer (int32 type, int32 id, FIDString _function, FIDString style) +{ + return startLayer (type, id, _function, style, false); +} + +//------------------------------------------------------------------------ +bool XmlRepresentationHelper::endLayer () +{ + if (!checkState (kInCell)) + return false; + + StringWriter writer (stream); + String string; + string.printf ("%s", END_TAG_STRING(LAYER_TAG)); + writer.write (string); + writer.write (ENDLINE_A); + + return true; +} + +//------------------------------------------------------------------------ +bool XmlRepresentationHelper::startEndLayer (int32 type, int32 id, FIDString _function, FIDString style) +{ + return startLayer (type, id, _function, style, true); +} + +//------------------------------------------------------------------------ +bool XmlRepresentationHelper::startLayer (int32 type, int32 id, FIDString _function, FIDString style, bool ended) +{ + if (!checkState (kInLayer)) + return false; + + StringWriter writer (stream); + String string; + + string.printf ("<%s %s=\"%s\" %s=\"%d\"", LAYER_TAG, ATTR_TYPE, Vst::LayerType::layerTypeFIDString[type], ATTR_PARAMID, id); + writer.write (string); + + if (_function) + { + string.printf (" %s=\"%s\"", Vst::Attributes::kFunction, _function); + writer.write (string); + } + if (style) + { + string = Vst::LayerType::layerTypeFIDString[type]; + if (type == Vst::LayerType::kSwitch) + string.printf (" %s=\"%s\"", Vst::Attributes::kSwitchStyle, style); + else if (type == Vst::LayerType::kLED) + string.printf (" %s=\"%s\"", Vst::Attributes::kLEDStyle, style); + else + string.printf (" %s=\"%s\"", Vst::Attributes::kStyle, style); + writer.write (string); + } + + if (ended) + { + writer.write ("/>"); + if (!checkState (kInCell)) + return false; + } + else + writer.write (">"); + writer.write (ENDLINE_A); + + return true; +} + +//------------------------------------------------------------------------ +bool XmlRepresentationHelper::startEndCellOneLayer (int32 type, int32 id, FIDString _function, FIDString style) +{ + if (!startCell ()) + return false; + startEndLayer (type, id, _function, style); + endCell (); + + return true; +} + +//------------------------------------------------------------------------ +bool XmlRepresentationHelper::startLayer (Vst::ParameterInfo& info, FIDString _function) +{ + return startLayer (info, _function, false); +} + +//------------------------------------------------------------------------ +bool XmlRepresentationHelper::startLayer (Vst::ParameterInfo& info, FIDString _function, bool ended) +{ + FIDString style = 0; + int32 type = Vst::LayerType::kKnob; + if (info.flags & Vst::ParameterInfo::kIsReadOnly) + type = Vst::LayerType::kLED; + else if (info.stepCount == 1) + { + type = Vst::LayerType::kSwitch; + style = Vst::AttributesStyle::kSwitchPushIncLoopedStyle; + } + + return startLayer (type, info.id, _function, style, ended); +} + +//------------------------------------------------------------------------ +bool XmlRepresentationHelper::startEndLayer (Vst::ParameterInfo& info, FIDString _function) +{ + return startLayer (info, _function, true); +} + +//------------------------------------------------------------------------ +bool XmlRepresentationHelper::startEndCellOneLayer (Vst::ParameterInfo& info, FIDString _function) +{ + if (!startCell ()) + return false; + startEndLayer (info, _function); + endCell (); + + return true; +} + +//------------------------------------------------------------------------ +bool XmlRepresentationHelper::startEndCellOneLayerWithParamName (Vst::ParameterInfo& info, FIDString _function /*= 0*/) +{ + if (!startCell ()) + return false; + startLayer (info, _function, false); + startEndTitleDisplay (info); + endLayer (); + endCell (); + + return true; +} + +//------------------------------------------------------------------------ +bool XmlRepresentationHelper::startEndTitleDisplay (Vst::ParameterInfo& info) +{ + String nameString (info.title); + + if (nameString.isEmpty ()) + return false; + + if (!checkState (kInTitleDisplay)) + return false; + + StringWriter writer (stream); + String string; + + string.printf ("<%s>", TITLEDISPLAY_TAG); + writer.write (string); + writer.write (ENDLINE_A); + + // start of name scope + + if (!checkState (kInName)) + { + string.printf ("%s", END_TAG_STRING (TITLEDISPLAY_TAG)); + writer.write (string); + writer.write (ENDLINE_A); + return false; + } + + string.printf ("<%s>%s", NAME_TAG, nameString.text8 (), NAME_TAG); + writer.write (string); + writer.write (ENDLINE_A); + + if (nameString.length () > MEDIUM_TITLE_LIMIT) + { + nameString.assign (info.shortTitle); + + if (! nameString.isEmpty ()) + { + nameString.removeChars (); // remove space + + if (nameString.length () > MEDIUM_TITLE_LIMIT) + nameString.remove (MEDIUM_TITLE_LIMIT); // Trimming the rest to get a short string + + string.printf ("<%s>%s", NAME_TAG, nameString.text8 (), NAME_TAG); + writer.write (string); + writer.write (ENDLINE_A); + } + } + + if (nameString.length () > SHORT_TITLE_LIMIT) + { + nameString.remove (SHORT_TITLE_LIMIT); // Trimming the rest to get a short string + + string.printf ("<%s>%s", NAME_TAG, nameString.text8 (), NAME_TAG); + writer.write (string); + writer.write (ENDLINE_A); + } + + if (!checkState (kInTitleDisplay)) + return false; + + // end of name scope + + string.printf ("%s", END_TAG_STRING (TITLEDISPLAY_TAG)); + writer.write (string); + writer.write (ENDLINE_A); + + if (!checkState (kInLayer)) + return false; + + return true; +} + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vstrepresentation.h b/source/includes/vst3sdk/public.sdk/source/vst/vstrepresentation.h new file mode 100644 index 000000000..cf9e36748 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vstrepresentation.h @@ -0,0 +1,168 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vstrepresentation.h +// Created by : Steinberg, 08/2010 +// Description : VST Representation Helper +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "pluginterfaces/vst/ivstrepresentation.h" +#include "pluginterfaces/vst/ivsteditcontroller.h" + +//------------------------------------------------------------------------ +namespace Steinberg { +class IBStreamer; + +namespace Vst { + +//------------------------------------------------------------------------ +/** Helper for XML Representation creation. +\ingroup vstClasses + +Here an example of how to use this helper: +\n +\code + +// here the parameter ids used by this example +enum { + kGain = 129, + kSize, + kCutoff, + kResonance, + kMaster, + kEnable1, + kEnable2, + kFrequency1, + kFrequency2, + kGain1, + kGain2, +}; + +//------------------------------------------------------------------------ +tresult PLUGIN_API MyPlugInController::getXmlRepresentationStream (Vst::RepresentationInfo& info, IBStream* stream) +{ + String name (info.name); + if (name == GENERIC_8_CELLS) + { + Vst::XmlRepresentationHelper helper (info, "My Company Name", "My Product Name", gPlugProcessorClassID, stream); + + helper.startPage ("Main Page"); + helper.startEndCellOneLayer (Vst::LayerType::kKnob, kGain); + helper.startEndCellOneLayer (Vst::LayerType::kKnob, kSize); + helper.startEndCellOneLayer (Vst::LayerType::kKnob, kMaster); + helper.startEndCell (); // empty cell + helper.startEndCellOneLayer (Vst::LayerType::kKnob, kCutoff); + helper.startEndCellOneLayer (Vst::LayerType::kKnob, kResonance); + helper.startEndCell (); // empty cell + helper.startEndCell (); // empty cell + helper.endPage (); + + helper.startPage ("Page 2"); + helper.startEndCellOneLayer (Vst::LayerType::kSwitch, kEnable1); + helper.startEndCellOneLayer (Vst::LayerType::kKnob, kFrequency1); + helper.startEndCellOneLayer (Vst::LayerType::kKnob, kGain1); + helper.startEndCell (); // empty + helper.startEndCellOneLayer (Vst::LayerType::kSwitch, kEnable2); + helper.startEndCellOneLayer (Vst::LayerType::kKnob, kFrequency2); + helper.startEndCellOneLayer (Vst::LayerType::kKnob, kGain2); + helper.startEndCell (); // empty + helper.endPage (); + + return kResultTrue; + } + return kResultFalse; +} +\endcode +*/ +//------------------------------------------------------------------------ +class XmlRepresentationHelper +{ +public: + XmlRepresentationHelper (const RepresentationInfo& info, const FIDString companyName, const FIDString pluginName, const TUID& pluginUID, IBStream* stream); + virtual ~XmlRepresentationHelper (); + + bool startPage (FIDString name, int32 unitID = -1); ///< Starts a Page before adding a Cell. + bool endPage (); ///< Ends a Page before opening a new one. + bool startCell (); ///< Starts a Cell before adding a Layer. + bool endCell (); ///< Ends a Cell when no more layer needed. + bool startEndCell (); ///< Creates an empty cell (alignment for example). + + /** Starts a layer for a given type (Vst::LayerType), a parameter id, optionally a function (Vst::AttributesFunction) + and a style (Vst::AttributesStyle). */ + bool startLayer (int32 type, int32 id, FIDString _function = 0, FIDString style = 0); + + /** Ends a layer before adding new one */ + bool endLayer (); + + /** Same than startLayer except that the layer will be ended automatically (no need to call endLayer). */ + bool startEndLayer (int32 type, int32 id, FIDString _function = 0, FIDString style = 0); + + /** Creates a Cell with 1 Layer and end it, could be only call after a call to startPage */ + bool startEndCellOneLayer (int32 type, int32 id, FIDString _function = 0, FIDString style = 0); + + /** Starts a layer for a given parameter info and an optional function (Vst::AttributesFunction). */ + bool startLayer (Vst::ParameterInfo& info, FIDString _function = 0); + + /** Same than startLayer with end created automatically. */ + bool startEndLayer (Vst::ParameterInfo& info, FIDString _function = 0); + + /** Creates a Cell with 1 Layer and end it, could be only call after a call to startPage. */ + bool startEndCellOneLayer (Vst::ParameterInfo& info, FIDString _function = 0); + + /** Creates a Cell with 1 Layer (with name) and end it, could be only call after a call to startPage. */ + bool startEndCellOneLayerWithParamName (Vst::ParameterInfo& info, FIDString _function = 0); + +protected: + enum { + kInRepresentation = 0, + kInPage, + kInCell, + kInLayer, + kInTitleDisplay, + kInName + }; + + bool startLayer (int32 type, int32 id, FIDString _function, FIDString style, bool ended); + bool startLayer (Vst::ParameterInfo& info, FIDString _function, bool ended); + + bool startEndTitleDisplay (Vst::ParameterInfo& info); + + bool checkState (int32 newState); + + IPtr stream; + int32 state; +}; + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vstsinglecomponenteffect.cpp b/source/includes/vst3sdk/public.sdk/source/vst/vstsinglecomponenteffect.cpp new file mode 100644 index 000000000..1773c5c6b --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vstsinglecomponenteffect.cpp @@ -0,0 +1,275 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vstsinglecomponenteffect.cpp +// Created by : Steinberg, 03/2008 +// Description : Basic Audio Effect Implementation in one component +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "vstsinglecomponenteffect.h" + +//----------------------------------------------------------------------------- +namespace Steinberg { +namespace Vst { + +//----------------------------------------------------------------------------- +// SingleComponentEffect Implementation +//----------------------------------------------------------------------------- +SingleComponentEffect::SingleComponentEffect () +: audioInputs (kAudio, kInput) +, audioOutputs (kAudio, kOutput) +, eventInputs (kEvent, kInput) +, eventOutputs (kEvent, kOutput) +{ + processSetup.maxSamplesPerBlock = 1024; + processSetup.processMode = Vst::kRealtime; + processSetup.sampleRate = 44100.0; + processSetup.symbolicSampleSize = Vst::kSample32; +} + +//----------------------------------------------------------------------------- +SingleComponentEffect::~SingleComponentEffect () +{ +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API SingleComponentEffect::initialize (FUnknown* context) +{ + return EditController::initialize (context); +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API SingleComponentEffect::terminate () +{ + parameters.removeAll (); + removeAllBusses (); + + return EditController::terminate (); +} + +//----------------------------------------------------------------------------- +int32 PLUGIN_API SingleComponentEffect::getBusCount (MediaType type, BusDirection dir) +{ + BusList* busList = getBusList (type, dir); + return busList ? static_cast(busList->size ()) : 0; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API SingleComponentEffect::getBusInfo (MediaType type, BusDirection dir, int32 index, + BusInfo& info) +{ + BusList* busList = getBusList (type, dir); + if (busList == 0) + return kInvalidArgument; + if (index >= static_cast (busList->size ())) + return kInvalidArgument; + + Bus* bus = busList->at (index); + info.mediaType = type; + info.direction = dir; + if (bus->getInfo (info)) + return kResultTrue; + return kResultFalse; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API SingleComponentEffect::activateBus (MediaType type, BusDirection dir, + int32 index, TBool state) +{ + BusList* busList = getBusList (type, dir); + Bus* bus = busList ? (Bus*)busList->at (index) : 0; + if (bus) + { + bus->setActive (state); + return kResultTrue; + } + return kResultFalse; +} + +//----------------------------------------------------------------------------- +AudioBus* SingleComponentEffect::addAudioInput (const TChar* name, SpeakerArrangement arr, + BusType busType, int32 flags) +{ + AudioBus* newBus = new AudioBus (name, busType, flags, arr); + audioInputs.push_back (IPtr (newBus, false)); + return newBus; +} + +//----------------------------------------------------------------------------- +AudioBus* SingleComponentEffect::addAudioOutput (const TChar* name, SpeakerArrangement arr, + BusType busType, int32 flags) +{ + AudioBus* newBus = new AudioBus (name, busType, flags, arr); + audioOutputs.push_back (IPtr (newBus, false)); + return newBus; +} + +//----------------------------------------------------------------------------- +EventBus* SingleComponentEffect::addEventInput (const TChar* name, int32 channels, BusType busType, + int32 flags) +{ + EventBus* newBus = new EventBus (name, busType, flags, channels); + eventInputs.push_back (IPtr (newBus, false)); + return newBus; +} + +//----------------------------------------------------------------------------- +EventBus* SingleComponentEffect::addEventOutput (const TChar* name, int32 channels, BusType busType, + int32 flags) +{ + EventBus* newBus = new EventBus (name, busType, flags, channels); + eventOutputs.push_back (IPtr (newBus, false)); + return newBus; +} + +//----------------------------------------------------------------------------- +tresult SingleComponentEffect::removeAudioBusses () +{ + audioInputs.clear (); + audioOutputs.clear (); + + return kResultOk; +} + +//----------------------------------------------------------------------------- +tresult SingleComponentEffect::removeEventBusses () +{ + eventInputs.clear (); + eventOutputs.clear (); + + return kResultOk; +} + +//----------------------------------------------------------------------------- +tresult SingleComponentEffect::removeAllBusses () +{ + removeAudioBusses (); + removeEventBusses (); + + return kResultOk; +} + +//----------------------------------------------------------------------------- +// IAudioProcessor +//----------------------------------------------------------------------------- +tresult PLUGIN_API SingleComponentEffect::setBusArrangements (SpeakerArrangement* inputs, + int32 numIns, + SpeakerArrangement* outputs, + int32 numOuts) +{ + if (numIns < 0 || numOuts < 0) + return kInvalidArgument; + + if (numIns > static_cast (audioInputs.size ()) || + numOuts > static_cast (audioOutputs.size ())) + return kResultFalse; + + for (int32 index = 0; index < static_cast(audioInputs.size ()); ++index) + { + if (index >= numIns) + break; + FCast (audioInputs[index].get ())->setArrangement (inputs[index]); + } + + for (int32 index = 0; index < static_cast(audioOutputs.size ()); ++index) + { + if (index >= numOuts) + break; + FCast (audioOutputs[index].get ())->setArrangement (outputs[index]); + } + + return kResultTrue; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API SingleComponentEffect::getBusArrangement (BusDirection dir, int32 busIndex, + SpeakerArrangement& arr) +{ + BusList* busList = getBusList (kAudio, dir); + AudioBus* audioBus = busList ? FCast (busList->at (busIndex)) : 0; + if (audioBus) + { + arr = audioBus->getArrangement (); + return kResultTrue; + } + return kResultFalse; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API SingleComponentEffect::setupProcessing (ProcessSetup& newSetup) +{ + if (canProcessSampleSize (newSetup.symbolicSampleSize) != kResultTrue) + return kResultFalse; + + processSetup = newSetup; + return kResultOk; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API SingleComponentEffect::canProcessSampleSize (int32 symbolicSampleSize) +{ + return symbolicSampleSize == kSample32 ? kResultTrue : kResultFalse; +} + +//----------------------------------------------------------------------------- +BusList* SingleComponentEffect::getBusList (MediaType type, BusDirection dir) +{ + if (type == kAudio) + return dir == kInput ? &audioInputs : &audioOutputs; + else if (type == kEvent) + return dir == kInput ? &eventInputs : &eventOutputs; + return 0; +} + +//----------------------------------------------------------------------------- +tresult PLUGIN_API SingleComponentEffect::queryInterface (const TUID iid, void** obj) +{ + if (memcmp (iid, IConnectionPoint::iid, sizeof (::Steinberg::TUID)) == 0) + { + // no need to expose IConnectionPoint to the host + return kNoInterface; + } + DEF_INTERFACE (IComponent) + DEF_INTERFACE (IAudioProcessor) + return EditController::queryInterface (iid, obj); +} + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg + +// work around for the name clash of IComponent::setState and IEditController::setState +// make sure that vsteditcontroller.cpp is otherwise excluded from your project +#define setState setEditorState +#define getState getEditorState +#include "public.sdk/source/vst/vsteditcontroller.cpp" +#undef setState +#undef getState diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vstsinglecomponenteffect.h b/source/includes/vst3sdk/public.sdk/source/vst/vstsinglecomponenteffect.h new file mode 100644 index 000000000..835f97837 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vstsinglecomponenteffect.h @@ -0,0 +1,148 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : plublic.sdk/source/vst/vstsinglecomponenteffect.h +// Created by : Steinberg, 03/2008 +// Description : Recombination class of Audio Effect and Edit Controller +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "pluginterfaces/vst/ivstaudioprocessor.h" + +// work around for the name clash of IComponent::setState and IEditController::setState +#define setState setEditorState +#define getState getEditorState +#include "public.sdk/source/vst/vsteditcontroller.h" +#include "pluginterfaces/vst/ivsteditcontroller.h" +#undef setState +#undef getState + +#include "public.sdk/source/vst/vstbus.h" +#include "public.sdk/source/vst/vstparameters.h" + +//------------------------------------------------------------------------ +namespace Steinberg { +namespace Vst { + +//------------------------------------------------------------------------ +/** Default implementation for a non-distributable Plug-in that combines +processor and edit controller in one component. +\ingroup vstClasses +- [released: 3.0.2] + +This can be used as base class for a VST 3 effect implementation in case that the standard way of +defining two separate components would cause too many implementation difficulties: +- Cubase 4.2 is the first host that supports combined VST 3 Plug-ins +- Use this class only after giving the standard way of defining two components +serious considerations! */ +//------------------------------------------------------------------------ +class SingleComponentEffect : public EditController, public IComponent, public IAudioProcessor +{ +public: +//------------------------------------------------------------------------ + SingleComponentEffect (); + virtual ~SingleComponentEffect (); + + //---from IPluginBase--------- + tresult PLUGIN_API initialize (FUnknown* context) SMTG_OVERRIDE; + tresult PLUGIN_API terminate () SMTG_OVERRIDE; + + //---from IComponent----------------------- + virtual tresult PLUGIN_API getControllerClassId (TUID classId) SMTG_OVERRIDE + { + return kNotImplemented; + } + virtual tresult PLUGIN_API setIoMode (IoMode mode) SMTG_OVERRIDE { return kNotImplemented; } + virtual int32 PLUGIN_API getBusCount (MediaType type, BusDirection dir) SMTG_OVERRIDE; + virtual tresult PLUGIN_API getBusInfo (MediaType type, BusDirection dir, int32 index, + BusInfo& bus /*out*/) SMTG_OVERRIDE; + virtual tresult PLUGIN_API getRoutingInfo (RoutingInfo& inInfo, + RoutingInfo& outInfo /*out*/) SMTG_OVERRIDE + { + return kNotImplemented; + } + virtual tresult PLUGIN_API activateBus (MediaType type, BusDirection dir, int32 index, + TBool state) SMTG_OVERRIDE; + virtual tresult PLUGIN_API setActive (TBool state) SMTG_OVERRIDE { return kResultOk; } + virtual tresult PLUGIN_API setState (IBStream* state) SMTG_OVERRIDE { return kNotImplemented; } + virtual tresult PLUGIN_API getState (IBStream* state) SMTG_OVERRIDE { return kNotImplemented; } + + // bus setup methods + AudioBus* addAudioInput (const TChar* name, SpeakerArrangement arr, BusType busType = kMain, + int32 flags = BusInfo::kDefaultActive); + + AudioBus* addAudioOutput (const TChar* name, SpeakerArrangement arr, BusType busType = kMain, + int32 flags = BusInfo::kDefaultActive); + + EventBus* addEventInput (const TChar* name, int32 channels = 16, BusType busType = kMain, + int32 flags = BusInfo::kDefaultActive); + + EventBus* addEventOutput (const TChar* name, int32 channels = 16, BusType busType = kMain, + int32 flags = BusInfo::kDefaultActive); + + tresult removeAudioBusses (); + tresult removeEventBusses (); + tresult removeAllBusses (); + + //---from IAudioProcessor ------------------- + virtual tresult PLUGIN_API setBusArrangements (SpeakerArrangement* inputs, int32 numIns, + SpeakerArrangement* outputs, + int32 numOuts) SMTG_OVERRIDE; + virtual tresult PLUGIN_API getBusArrangement (BusDirection dir, int32 index, + SpeakerArrangement& arr) SMTG_OVERRIDE; + virtual tresult PLUGIN_API canProcessSampleSize (int32 symbolicSampleSize) SMTG_OVERRIDE; + virtual uint32 PLUGIN_API getLatencySamples () SMTG_OVERRIDE { return 0; } + virtual tresult PLUGIN_API setupProcessing (ProcessSetup& setup) SMTG_OVERRIDE; + virtual tresult PLUGIN_API setProcessing (TBool state) SMTG_OVERRIDE { return kNotImplemented; } + virtual tresult PLUGIN_API process (ProcessData& data) SMTG_OVERRIDE { return kNotImplemented; } + virtual uint32 PLUGIN_API getTailSamples () SMTG_OVERRIDE { return kNoTail; } + + //---Interface--------- + OBJ_METHODS (SingleComponentEffect, EditController) + tresult PLUGIN_API queryInterface (const TUID iid, void** obj) SMTG_OVERRIDE; + REFCOUNT_METHODS (EditController) + +//------------------------------------------------------------------------ +protected: + BusList* getBusList (MediaType type, BusDirection dir); + + ProcessSetup processSetup; + + BusList audioInputs; + BusList audioOutputs; + BusList eventInputs; + BusList eventOutputs; +}; + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst/vstspeakerarray.h b/source/includes/vst3sdk/public.sdk/source/vst/vstspeakerarray.h new file mode 100644 index 000000000..126d6c515 --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst/vstspeakerarray.h @@ -0,0 +1,101 @@ +//----------------------------------------------------------------------------- +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst/vstspeakerarray,.h +// Created by : Steinberg, 04/2015 +// Description : Helper class representing speaker arrangement as array of speaker types. +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "pluginterfaces/vst/vsttypes.h" + +//------------------------------------------------------------------------ +namespace Steinberg { +namespace Vst { + +//------------------------------------------------------------------------ +// SpeakerArray +/** Helper class representing speaker arrangement as array of speaker types. */ +//------------------------------------------------------------------------ +class SpeakerArray +{ +public: +//------------------------------------------------------------------------ + SpeakerArray (SpeakerArrangement arr = 0) + { + setArrangement (arr); + } + + enum { kMaxSpeakers = 64 }; + + typedef uint64 SpeakerType; + + int32 total () const { return count; } + SpeakerType at (int32 index) const { return speaker[index]; } + + void setArrangement (SpeakerArrangement arr) + { + count = 0; + memset (speaker, 0, sizeof (speaker)); + + for (int32 i = 0; i < kMaxSpeakers; i++) + { + SpeakerType mask = 1ll << i; + if (arr & mask) + speaker[count++] = mask; + } + } + + SpeakerArrangement getArrangement () const + { + SpeakerArrangement arr = 0; + for (int32 i = 0; i < count; i++) + arr |= speaker[i]; + return arr; + } + + int32 getSpeakerIndex (SpeakerType which) const + { + for (int32 i = 0; i < count; i++) + if (speaker[i] == which) + return i; + return -1; + } +//------------------------------------------------------------------------ +protected: + int32 count; + SpeakerType speaker[kMaxSpeakers]; +}; + +//------------------------------------------------------------------------ +} // namespace Vst +} // namespace Steinberg diff --git a/source/includes/vst3sdk/public.sdk/source/vst3stdsdk.cpp b/source/includes/vst3sdk/public.sdk/source/vst3stdsdk.cpp new file mode 100644 index 000000000..b9abf6b4f --- /dev/null +++ b/source/includes/vst3sdk/public.sdk/source/vst3stdsdk.cpp @@ -0,0 +1,42 @@ +//------------------------------------------------------------------------ +// Project : VST SDK +// +// Category : Helpers +// Filename : public.sdk/source/vst3stdsdk.cpp +// Created by : Steinberg, 09/2008 +// Description : include basic helpers cpp files +// +//----------------------------------------------------------------------------- +// LICENSE +// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "public.sdk/source/vst/vstaudioeffect.cpp" +#include "public.sdk/source/vst/vstcomponent.cpp" +#include "public.sdk/source/vst/vstcomponentbase.cpp" +#include "public.sdk/source/vst/vsteditcontroller.cpp" +#include "public.sdk/source/vst/vstbus.cpp" +#include "public.sdk/source/vst/vstparameters.cpp"