| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| #pragma once | ||
|
|
||
| #include "../plugin.h" | ||
|
|
||
| static CLAP_CONSTEXPR const char CLAP_EXT_LATENCY[] = "clap.latency"; | ||
|
|
||
| #ifdef __cplusplus | ||
| extern "C" { | ||
| #endif | ||
|
|
||
| // The audio ports scan has to be done while the plugin is deactivated. | ||
| typedef struct clap_plugin_latency { | ||
| // Returns the plugin latency. | ||
| // [main-thread] | ||
| uint32_t(CLAP_ABI *get)(const clap_plugin_t *plugin); | ||
| } clap_plugin_latency_t; | ||
|
|
||
| typedef struct clap_host_latency { | ||
| // Tell the host that the latency changed. | ||
| // The latency is only allowed to change if the plugin is deactivated. | ||
| // If the plugin is activated, call host->request_restart() | ||
| // [main-thread] | ||
| void(CLAP_ABI *changed)(const clap_host_t *host); | ||
| } clap_host_latency_t; | ||
|
|
||
| #ifdef __cplusplus | ||
| } | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| #pragma once | ||
|
|
||
| #include "../plugin.h" | ||
| #include "../string-sizes.h" | ||
|
|
||
| /// @page Note Ports | ||
| /// | ||
| /// This extension provides a way for the plugin to describe its current note ports. | ||
| /// If the plugin does not implement this extension, it won't have note input or output. | ||
| /// The plugin is only allowed to change its note ports configuration while it is deactivated. | ||
|
|
||
| static CLAP_CONSTEXPR const char CLAP_EXT_NOTE_PORTS[] = "clap.note-ports"; | ||
|
|
||
| #ifdef __cplusplus | ||
| extern "C" { | ||
| #endif | ||
|
|
||
| enum clap_note_dialect { | ||
| // Uses clap_event_note and clap_event_note_expression. | ||
| CLAP_NOTE_DIALECT_CLAP = 1 << 0, | ||
|
|
||
| // Uses clap_event_midi, no polyphonic expression | ||
| CLAP_NOTE_DIALECT_MIDI = 1 << 1, | ||
|
|
||
| // Uses clap_event_midi, with polyphonic expression (MPE) | ||
| CLAP_NOTE_DIALECT_MIDI_MPE = 1 << 2, | ||
|
|
||
| // Uses clap_event_midi2 | ||
| CLAP_NOTE_DIALECT_MIDI2 = 1 << 3, | ||
| }; | ||
|
|
||
| typedef struct clap_note_port_info { | ||
| // id identifies a port and must be stable. | ||
| // id may overlap between input and output ports. | ||
| clap_id id; | ||
| uint32_t supported_dialects; // bitfield, see clap_note_dialect | ||
| uint32_t preferred_dialect; // one value of clap_note_dialect | ||
| char name[CLAP_NAME_SIZE]; // displayable name, i18n? | ||
| } clap_note_port_info_t; | ||
|
|
||
| // The note ports scan has to be done while the plugin is deactivated. | ||
| typedef struct clap_plugin_note_ports { | ||
| // number of ports, for either input or output | ||
| // [main-thread] | ||
| uint32_t(CLAP_ABI *count)(const clap_plugin_t *plugin, bool is_input); | ||
|
|
||
| // get info about about a note port. | ||
| // [main-thread] | ||
| bool(CLAP_ABI *get)(const clap_plugin_t *plugin, | ||
| uint32_t index, | ||
| bool is_input, | ||
| clap_note_port_info_t *info); | ||
| } clap_plugin_note_ports_t; | ||
|
|
||
| enum { | ||
| // The ports have changed, the host shall perform a full scan of the ports. | ||
| // This flag can only be used if the plugin is not active. | ||
| // If the plugin active, call host->request_restart() and then call rescan() | ||
| // when the host calls deactivate() | ||
| CLAP_NOTE_PORTS_RESCAN_ALL = 1 << 0, | ||
|
|
||
| // The ports name did change, the host can scan them right away. | ||
| CLAP_NOTE_PORTS_RESCAN_NAMES = 1 << 1, | ||
| }; | ||
|
|
||
| typedef struct clap_host_note_ports { | ||
| // Query which dialects the host supports | ||
| // [main-thread] | ||
| uint32_t(CLAP_ABI *supported_dialects)(const clap_host_t *host); | ||
|
|
||
| // Rescan the full list of note ports according to the flags. | ||
| // [main-thread] | ||
| void(CLAP_ABI *rescan)(const clap_host_t *host, uint32_t flags); | ||
| } clap_host_note_ports_t; | ||
|
|
||
| #ifdef __cplusplus | ||
| } | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| #pragma once | ||
|
|
||
| #include "../plugin.h" | ||
| #include "../stream.h" | ||
|
|
||
| static CLAP_CONSTEXPR const char CLAP_EXT_STATE[] = "clap.state"; | ||
|
|
||
| #ifdef __cplusplus | ||
| extern "C" { | ||
| #endif | ||
|
|
||
| typedef struct clap_plugin_state { | ||
| // Saves the plugin state into stream. | ||
| // Returns true if the state was correctly saved. | ||
| // [main-thread] | ||
| bool(CLAP_ABI *save)(const clap_plugin_t *plugin, const clap_ostream_t *stream); | ||
|
|
||
| // Loads the plugin state from stream. | ||
| // Returns true if the state was correctly restored. | ||
| // [main-thread] | ||
| bool(CLAP_ABI *load)(const clap_plugin_t *plugin, const clap_istream_t *stream); | ||
| } clap_plugin_state_t; | ||
|
|
||
| typedef struct clap_host_state { | ||
| // Tell the host that the plugin state has changed and should be saved again. | ||
| // If a parameter value changes, then it is implicit that the state is dirty. | ||
| // [main-thread] | ||
| void(CLAP_ABI *mark_dirty)(const clap_host_t *host); | ||
| } clap_host_state_t; | ||
|
|
||
| #ifdef __cplusplus | ||
| } | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| #pragma once | ||
|
|
||
| #include "../plugin.h" | ||
|
|
||
| static CLAP_CONSTEXPR const char CLAP_EXT_THREAD_CHECK[] = "clap.thread-check"; | ||
|
|
||
| #ifdef __cplusplus | ||
| extern "C" { | ||
| #endif | ||
|
|
||
| /// @page thread-check | ||
| /// | ||
| /// CLAP defines two symbolic threads: | ||
| /// | ||
| /// main-thread: | ||
| /// This is the thread in which most of the interaction between the plugin and host happens. | ||
| /// It is usually the thread on which the GUI receives its events. | ||
| /// It isn't a realtime thread, yet this thread needs to respond fast enough to user interaction, | ||
| /// so it is recommended to run long and expensive tasks such as preset indexing or asset loading | ||
| /// in dedicated background threads. | ||
| /// | ||
| /// audio-thread: | ||
| /// This thread is used for realtime audio processing. Its execution should be as deterministic | ||
| /// as possible to meet the audio interface's deadline (can be <1ms). In other words, there is a | ||
| /// known set of operations that should be avoided: malloc() and free(), mutexes (spin mutexes | ||
| /// are worse), I/O, waiting, ... | ||
| /// The audio-thread is something symbolic, there isn't one OS thread that remains the | ||
| /// audio-thread for the plugin lifetime. As you may guess, the host is likely to have a | ||
| /// thread pool and the plugin.process() call may be scheduled on different OS threads over time. | ||
| /// The most important thing is that there can't be two audio-threads at the same time. All the | ||
| /// functions marked with [audio-thread] **ARE NOT CONCURRENT**. The host may mark any OS thread, | ||
| /// including the main-thread as the audio-thread, as long as it can guarentee that only one OS | ||
| /// thread is the audio-thread at a time. The audio-thread can be seen as a concurrency guard for | ||
| /// all functions marked with [audio-thread]. | ||
|
|
||
| // This interface is useful to do runtime checks and make | ||
| // sure that the functions are called on the correct threads. | ||
| // It is highly recommended that hosts implement this extension. | ||
| typedef struct clap_host_thread_check { | ||
| // Returns true if "this" thread is the main thread. | ||
| // [thread-safe] | ||
| bool(CLAP_ABI *is_main_thread)(const clap_host_t *host); | ||
|
|
||
| // Returns true if "this" thread is one of the audio threads. | ||
| // [thread-safe] | ||
| bool(CLAP_ABI *is_audio_thread)(const clap_host_t *host); | ||
| } clap_host_thread_check_t; | ||
|
|
||
| #ifdef __cplusplus | ||
| } | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| #pragma once | ||
|
|
||
| #include "../plugin.h" | ||
|
|
||
| static CLAP_CONSTEXPR const char CLAP_EXT_TIMER_SUPPORT[] = "clap.timer-support"; | ||
|
|
||
| #ifdef __cplusplus | ||
| extern "C" { | ||
| #endif | ||
|
|
||
| typedef struct clap_plugin_timer_support { | ||
| // [main-thread] | ||
| void(CLAP_ABI *on_timer)(const clap_plugin_t *plugin, clap_id timer_id); | ||
| } clap_plugin_timer_support_t; | ||
|
|
||
| typedef struct clap_host_timer_support { | ||
| // Registers a periodic timer. | ||
| // The host may adjust the period if it is under a certain threshold. | ||
| // 30 Hz should be allowed. | ||
| // [main-thread] | ||
| bool(CLAP_ABI *register_timer)(const clap_host_t *host, uint32_t period_ms, clap_id *timer_id); | ||
|
|
||
| // [main-thread] | ||
| bool(CLAP_ABI *unregister_timer)(const clap_host_t *host, clap_id timer_id); | ||
| } clap_host_timer_support_t; | ||
|
|
||
| #ifdef __cplusplus | ||
| } | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| #pragma once | ||
|
|
||
| #include "private/std.h" | ||
| #include "private/macros.h" | ||
|
|
||
| #ifdef __cplusplus | ||
| extern "C" { | ||
| #endif | ||
|
|
||
| typedef struct clap_istream { | ||
| void *ctx; // reserved pointer for the stream | ||
|
|
||
| // returns the number of bytes read; 0 indicates end of file and -1 a read error | ||
| int64_t(CLAP_ABI *read)(const struct clap_istream *stream, void *buffer, uint64_t size); | ||
| } clap_istream_t; | ||
|
|
||
| typedef struct clap_ostream { | ||
| void *ctx; // reserved pointer for the stream | ||
|
|
||
| // returns the number of bytes written; -1 on write error | ||
| int64_t(CLAP_ABI *write)(const struct clap_ostream *stream, const void *buffer, uint64_t size); | ||
| } clap_ostream_t; | ||
|
|
||
| #ifdef __cplusplus | ||
| } | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| Copyright 2020 Michael Fabian 'Xaymar' Dirks <info@xaymar.com> | ||
|
|
||
| Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: | ||
|
|
||
| 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. | ||
|
|
||
| 2. 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. | ||
|
|
||
| 3. Neither the name of the copyright holder 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 HOLDER 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. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| # About the Project | ||
| This is a completely "clean room" untainted reverse engineered "SDK" for the VST 2.x interface. It was reverse engineered from binaries where no license restricting the reverse engineering was attached, or where the legal system explicitly allowed reverse engineering for the purpose of interoperability. | ||
|
|
||
| # Frequently Asked Questions | ||
| ## Is this legal? Can I use this in my own product? | ||
| **Disclaimer:** I am not a lawyer. The information presented below is purely from available copyright laws that I could find about this topic. You should always consult with a lawyer first before including this in your product. | ||
|
|
||
| As this only enables interoperability with existing VST 2.x programs and addons, it is considered to be reverse engineering in the name of interoperability. In most of the developed world, this is considered completely legal and is fine to be used by anyone, as long as it is not the only function of the product. | ||
|
|
||
| Note that this does not grant any patent licenses, nor does it grant you any right to use trademarks in the names. That could mean that you can't advertise your product as having support for VST, and can't use VST in the name or presentation of the product at all unless you have permission to do so. | ||
|
|
||
| ## Why recreate an SDK for something officially abandoned by the creators? | ||
| There is a ton of software that is only capable of loading VST2.x audio effects, and Steinberg has made no effort to create a VST3-to-VST2-adapter for that software. Notable software includes Audacity and OBS Studio, which both likely felt restricted by the license additions Steinberg added to the GPL license. | ||
|
|
||
| ## How did you reverse engineer this? | ||
| The reverse engineering was done with various tools (mostly disassemblers to x86 assembly code), hooking into system APIs, attempting to mimic functionality through observation and testing, and other methods. Primarily Visual Studio Code was used to write the header files, and Visual Studio 2019 Express was used to create fake VST plugins/hosts to figure out actual behavior. | ||
|
|
||
| ### Which binaries were disassembled? | ||
| * A fake VST2 host (using this header) was created to verify against existing plugins. | ||
| * A fake VST2 plugin (using this header) was created to verify against existing hosts. | ||
| * OBS Studio and Audacity were used to verify compatability between closed source and open source VST hosts. |