Browse Source

Carla-Rack VST plugin

tags/1.9.5
falkTX 10 years ago
parent
commit
a4061abfec
6 changed files with 856 additions and 12 deletions
  1. +11
    -1
      Makefile
  2. +2
    -2
      source/carla_host.py
  3. +71
    -6
      source/modules/native-plugins/resources/carla-plugin
  4. +20
    -0
      source/plugin/Makefile
  5. +1
    -3
      source/plugin/carla-native-lv2.cpp
  6. +751
    -0
      source/plugin/carla-native-vst.cpp

+ 11
- 1
Makefile View File

@@ -145,7 +145,7 @@ bin/carla-native.lv2/manifest.ttl: bin/carla-native-lv2-export$(APP_EXT) bridges
cd bin/carla-native.lv2 && $(LINK) ../*bridge-* ../carla-discovery-* .; cd ..

bin/carla-native-lv2-export$(APP_EXT): bin/carla-native.lv2/carla-native$(LIB_EXT) .FORCE
$(MAKE) -C source/plugin ../../$@
$(MAKE) -C source/plugin

# --------------------------------------------------------------

@@ -378,6 +378,7 @@ install:
install -d $(DESTDIR)$(PREFIX)/lib/carla/styles/
install -d $(DESTDIR)$(PREFIX)/lib/lv2/carla-native.lv2/
install -d $(DESTDIR)$(PREFIX)/lib/lv2/carla-native.lv2/styles/
install -d $(DESTDIR)$(PREFIX)/lib/vst/
install -d $(DESTDIR)$(PREFIX)/lib/pkgconfig/
install -d $(DESTDIR)$(PREFIX)/include/carla/
install -d $(DESTDIR)$(PREFIX)/include/carla/includes/
@@ -445,6 +446,11 @@ install:
bin/carla-native.lv2/*.ttl \
$(DESTDIR)$(PREFIX)/lib/lv2/carla-native.lv2/

# Install vst plugin
install -m 644 \
bin/carla-native-vst*.* \
$(DESTDIR)$(PREFIX)/lib/vst/

# Install binaries (backend)
install -m 755 \
bin/*bridge-* \
@@ -547,6 +553,10 @@ install:
rm -rf $(DESTDIR)$(PREFIX)/lib/lv2/carla-native.lv2/resources
$(LINK) $(PREFIX)/share/carla/resources/ $(DESTDIR)$(PREFIX)/lib/lv2/carla-native.lv2/

# Link resources for vst plugin
rm -rf $(DESTDIR)$(PREFIX)/lib/vst/carla-resources
$(LINK) $(PREFIX)/lib/lv2/carla-native.lv2/resources $(DESTDIR)$(PREFIX)/lib/vst/carla-resources

# Adjust PREFIX value in script files
sed -i "s?X-PREFIX-X?$(PREFIX)?" \
$(DESTDIR)$(PREFIX)/bin/carla \


+ 2
- 2
source/carla_host.py View File

@@ -99,8 +99,8 @@ class HostWindow(QMainWindow):

# --------------------------------------------------------------------------------------------------------

def __init__(self, host):
QMainWindow.__init__(self)
def __init__(self, host, parent=None):
QMainWindow.__init__(self, parent)
self.host = host
self.ui = ui_carla_host.Ui_CarlaHostW()
self.ui.setupUi(self)


+ 71
- 6
source/modules/native-plugins/resources/carla-plugin View File

@@ -64,13 +64,13 @@ class PluginHost(CarlaHostPlugin):
# Main Window

class CarlaMiniW(ExternalUI, HostWindow):
def __init__(self, host):
def __init__(self, host, parent=None):
# need to init this first
gCarla.gui = self

# now the regular stuff
ExternalUI.__init__(self)
HostWindow.__init__(self, host)
HostWindow.__init__(self, host, parent)
self.host = host

if False:
@@ -94,10 +94,12 @@ class CarlaMiniW(ExternalUI, HostWindow):
# ExternalUI Callbacks

def d_uiShow(self):
self.show()
if self.parent() is None:
self.show()

def d_uiHide(self):
self.hide()
if self.parent() is None:
self.hide()

def d_uiQuit(self):
self.close()
@@ -292,6 +294,58 @@ class CarlaMiniW(ExternalUI, HostWindow):

return True

# ------------------------------------------------------------------------------------------------------------
# Embed plugin UI

if LINUX and not config_UseQt5:
from PyQt4.QtGui import QLabel, QHBoxLayout, QX11EmbedWidget

class CarlaEmbedW(QX11EmbedWidget):
def __init__(self, host, winId):
QX11EmbedWidget.__init__(self)
self.host = host

self.fWinId = winId

self.fLayout = QVBoxLayout(self)
self.fLayout.setContentsMargins(0, 0, 0, 0)
self.fLayout.setSpacing(0)
self.setLayout(self.fLayout)

gCarla.gui = CarlaMiniW(host, self)
gCarla.gui.hide()

gCarla.gui.ui.act_file_quit.setEnabled(False)
gCarla.gui.ui.menu_File.setEnabled(False)
gCarla.gui.ui.menu_File.setVisible(False)
#menuBar = gCarla.gui.menuBar()
#menuBar.removeAction(gCarla.gui.ui.menu_File.menuAction())

self.addWidget(gCarla.gui.menuBar())
self.addWidget(gCarla.gui.ui.toolBar)
self.addWidget(gCarla.gui.centralWidget())

self.setFixedSize(740, 512)
self.embedInto(winId)
self.show()

def addWidget(self, widget):
widget.setParent(self)
self.fLayout.addWidget(widget)

def showEvent(self, event):
QX11EmbedWidget.showEvent(self, event)

# set our gui as parent for all plugins UIs
winIdStr = "%x" % self.fWinId
self.host.set_engine_option(ENGINE_OPTION_FRONTEND_WIN_ID, 0, winIdStr)

def hideEvent(self, event):
# disable parent
self.host.set_engine_option(ENGINE_OPTION_FRONTEND_WIN_ID, 0, "0")

QX11EmbedWidget.hideEvent(self, event)

# ------------------------------------------------------------------------------------------------------------
# Main

@@ -310,7 +364,8 @@ if __name__ == '__main__':
# Init host backend

host = initHost("Carla-Plugin", PluginHost, False, True, True)
host.processMode = ENGINE_PROCESS_MODE_PATCHBAY if sys.argv[0].lower().endswith("/carla-plugin-patchbay") else ENGINE_PROCESS_MODE_CONTINUOUS_RACK
host.processMode = ENGINE_PROCESS_MODE_PATCHBAY if sys.argv[0].lower().endswith("/carla-plugin-patchbay") else ENGINE_PROCESS_MODE_CONTINUOUS_RACK
host.processModeForced = True

# FIXME
loadHostSettings(host)
@@ -318,7 +373,17 @@ if __name__ == '__main__':
# -------------------------------------------------------------
# Create GUI

gCarla.gui = CarlaMiniW(host)
try:
winId = int(os.getenv("CARLA_PLUGIN_EMBED_WINID"))
except:
winId = 0

host.setenv("CARLA_PLUGIN_EMBED_WINID", "0")

if LINUX and winId != 0 and not config_UseQt5:
gui = CarlaEmbedW(host, winId)
else:
gui = CarlaMiniW(host)

# -------------------------------------------------------------
# simulate an engire started callback FIXME


+ 20
- 0
source/plugin/Makefile View File

@@ -78,6 +78,14 @@ TARGETS = \
../../bin/carla-native.lv2/carla-native$(LIB_EXT) \
../../bin/carla-native-lv2-export$(APP_EXT)

ifeq ($(LINUX),true)
ifeq ($(DEFAULT_QT),4)
TARGETS += \
../../bin/carla-native-vstfx$(LIB_EXT) \
../../bin/carla-native-vstsyn$(LIB_EXT)
endif
endif

# --------------------------------------------------------------

all: $(TARGETS)
@@ -100,6 +108,12 @@ carla-native-lv2.cpp.o: carla-native-lv2.cpp carla-native-base.cpp ../modules/Ca
carla-native-lv2-export.cpp.o: carla-native-lv2-export.cpp carla-native-base.cpp ../modules/CarlaNative.h
$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@

carla-native-vst.cpp.fx.o: carla-native-vst.cpp carla-native-base.cpp ../modules/CarlaNative.h
$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@

carla-native-vst.cpp.syn.o: carla-native-vst.cpp carla-native-base.cpp ../modules/CarlaNative.h
$(CXX) $< $(BUILD_CXX_FLAGS) -DCARLA_PLUGIN_SYNTH -c -o $@

# --------------------------------------------------------------

../../bin/carla-native.lv2/carla-native$(LIB_EXT): carla-native-lv2.cpp.o $(LIBS)
@@ -108,4 +122,10 @@ carla-native-lv2-export.cpp.o: carla-native-lv2-export.cpp carla-native-base.cpp
../../bin/carla-native-lv2-export$(APP_EXT): carla-native-lv2-export.cpp.o $(LIBS)
$(CXX) $< $(LIBS_START) $(LIBS) $(LIBS_END) $(LINK_FLAGS) -o $@

../../bin/carla-native-vstfx$(LIB_EXT): carla-native-vst.cpp.fx.o $(LIBS)
$(CXX) $< $(LIBS_START) $(LIBS) $(LIBS_END) $(SHARED) $(LINK_FLAGS) -o $@

../../bin/carla-native-vstsyn$(LIB_EXT): carla-native-vst.cpp.syn.o $(LIBS)
$(CXX) $< $(LIBS_START) $(LIBS) $(LIBS_END) $(SHARED) $(LINK_FLAGS) -o $@

# --------------------------------------------------------------

+ 1
- 3
source/plugin/carla-native-lv2.cpp View File

@@ -200,9 +200,7 @@ public:
}

fHandle = fDescriptor->instantiate(&fHost);

if (fHandle == nullptr)
return false;
CARLA_SAFE_ASSERT_RETURN(fHandle != nullptr, false);

carla_zeroStruct<NativeMidiEvent>(fMidiEvents, kMaxMidiEvents*2);
carla_zeroStruct<NativeTimeInfo>(fTimeInfo);


+ 751
- 0
source/plugin/carla-native-vst.cpp View File

@@ -0,0 +1,751 @@
/*
* Carla Native Plugins
* Copyright (C) 2013-2014 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/

#define CARLA_NATIVE_PLUGIN_LV2
#include "carla-native-base.cpp"

#include "CarlaMathUtils.hpp"
#include "juce_core.h"

#ifdef VESTIGE_HEADER
# include "vestige/aeffectx.h"
#define effFlagsProgramChunks (1 << 5)
#define effGetParamLabel 6
#define effGetChunk 23
#define effSetChunk 24
#define effGetPlugCategory 35
#define kPlugCategEffect 1
#define kPlugCategSynth 2
#define kVstVersion 2400
struct ERect {
int16_t top, left, bottom, right;
};
#else
# include "vst/aeffectx.h"
#endif

uint32_t d_lastBufferSize = 0;
double d_lastSampleRate = 0.0;

// -----------------------------------------------------------------------

class NativePlugin
{
public:
static const uint32_t kMaxMidiEvents = 512;

NativePlugin(const audioMasterCallback audioMaster, AEffect* const effect, const NativePluginDescriptor* desc)
: fAudioMaster(audioMaster),
fEffect(effect),
fHandle(nullptr),
fHost(),
fDescriptor(desc),
fBufferSize(d_lastBufferSize),
fSampleRate(d_lastSampleRate),
fMidiEventCount(0),
fTimeInfo(),
fVstRect(),
fStateChunk(nullptr),
leakDetector_NativePlugin()
{
fHost.handle = this;
fHost.uiName = carla_strdup("CarlaVST");
fHost.uiParentId = 0;

// find resource dir
using juce::File;

File curExe = File::getSpecialLocation(File::currentExecutableFile).getLinkedTarget();
File resDir = curExe.getSiblingFile("carla-resources");
if (! resDir.exists())
resDir = curExe.getSiblingFile("resources");
if (! resDir.exists())
resDir = File("/usr/share/carla/resources/");

fHost.resourceDir = carla_strdup(resDir.getFullPathName().toRawUTF8());

fHost.get_buffer_size = host_get_buffer_size;
fHost.get_sample_rate = host_get_sample_rate;
fHost.is_offline = host_is_offline;
fHost.get_time_info = host_get_time_info;
fHost.write_midi_event = host_write_midi_event;
fHost.ui_parameter_changed = host_ui_parameter_changed;
fHost.ui_custom_data_changed = host_ui_custom_data_changed;
fHost.ui_closed = host_ui_closed;
fHost.ui_open_file = host_ui_open_file;
fHost.ui_save_file = host_ui_save_file;
fHost.dispatcher = host_dispatcher;

fVstRect.top = 0;
fVstRect.left = 0;
fVstRect.bottom = 512;
fVstRect.right = 740;

init();
}

~NativePlugin()
{
if (fDescriptor->cleanup != nullptr && fHandle != nullptr)
fDescriptor->cleanup(fHandle);

fHandle = nullptr;

if (fStateChunk != nullptr)
{
std::free(fStateChunk);
fStateChunk = nullptr;
}

if (fHost.uiName != nullptr)
{
delete[] fHost.uiName;
fHost.uiName = nullptr;
}

if (fHost.resourceDir != nullptr)
{
delete[] fHost.resourceDir;
fHost.resourceDir = nullptr;
}
}

bool init()
{
if (fDescriptor->instantiate == nullptr || fDescriptor->process == nullptr)
{
carla_stderr("Plugin is missing something...");
return false;
}

fHandle = fDescriptor->instantiate(&fHost);
CARLA_SAFE_ASSERT_RETURN(fHandle != nullptr, false);

carla_zeroStruct<NativeMidiEvent>(fMidiEvents, kMaxMidiEvents);
carla_zeroStruct<NativeTimeInfo>(fTimeInfo);

return true;
}

// -------------------------------------------------------------------

intptr_t vst_dispatcher(const int32_t opcode, const int32_t /*index*/, const intptr_t value, void* const ptr, const float opt)
{
CARLA_SAFE_ASSERT_RETURN(fHandle != nullptr, 0);

intptr_t ret = 0;

switch (opcode)
{
case effSetSampleRate:
if (carla_compareFloats(fSampleRate, static_cast<double>(opt)))
return 0;

fSampleRate = opt;

if (fDescriptor->dispatcher != nullptr)
fDescriptor->dispatcher(fHandle, PLUGIN_OPCODE_SAMPLE_RATE_CHANGED, 0, 0, nullptr, (float)fSampleRate);
break;

case effSetBlockSize:
if (fBufferSize == static_cast<uint32_t>(value))
return 0;

fBufferSize = static_cast<uint32_t>(value);

if (fDescriptor->dispatcher != nullptr)
fDescriptor->dispatcher(fHandle, PLUGIN_OPCODE_BUFFER_SIZE_CHANGED, 0, fBufferSize, nullptr, 0.0f);
break;

case effMainsChanged:
if (value != 0)
{
if (fDescriptor->activate != nullptr)
fDescriptor->activate(fHandle);

fMidiEventCount = 0;
carla_zeroStruct<NativeMidiEvent>(fMidiEvents, kMaxMidiEvents);
carla_zeroStruct<NativeTimeInfo>(fTimeInfo);
}
else
{
if (fDescriptor->deactivate != nullptr)
fDescriptor->deactivate(fHandle);
}
break;

case effEditGetRect:
*(ERect**)ptr = &fVstRect;
ret = 1;
break;

case effEditOpen:
if (fDescriptor->ui_show != nullptr)
{
char strBuf[0xff+1];
strBuf[0xff] = '\0';
std::snprintf(strBuf, 0xff, P_INTPTR, (intptr_t)ptr);

carla_setenv("CARLA_PLUGIN_EMBED_WINID", strBuf);

fDescriptor->ui_show(fHandle, true);

carla_setenv("CARLA_PLUGIN_EMBED_WINID", "0");

ret = 1;
}
break;

case effEditClose:
if (fDescriptor->ui_show != nullptr)
{
fDescriptor->ui_show(fHandle, false);
ret = 1;
}
break;

case effEditIdle:
if (fDescriptor->ui_idle != nullptr)
fDescriptor->ui_idle(fHandle);
break;

case effGetChunk:
if (ptr == nullptr || fDescriptor->get_state == nullptr)
return 0;

if (fStateChunk != nullptr)
std::free(fStateChunk);

fStateChunk = fDescriptor->get_state(fHandle);

if (fStateChunk == nullptr)
return 0;

ret = static_cast<intptr_t>(std::strlen(fStateChunk)+1);
*(void**)ptr = fStateChunk;
break;

case effSetChunk:
if (value <= 0 || fDescriptor->set_state == nullptr)
return 0;
if (value == 1)
return 1;

if (const char* const state = (const char*)ptr)
{
fDescriptor->set_state(fHandle, state);
ret = 1;
}
break;

case effProcessEvents:
if (const VstEvents* const events = (const VstEvents*)ptr)
{
if (events->numEvents == 0)
break;

for (int i=0, count=events->numEvents; i < count; ++i)
{
const VstMidiEvent* const vstMidiEvent((const VstMidiEvent*)events->events[i]);

if (vstMidiEvent == nullptr)
break;
if (vstMidiEvent->type != kVstMidiType || vstMidiEvent->deltaFrames < 0)
continue;
if (fMidiEventCount >= kMaxMidiEvents)
break;

fMidiEvents[fMidiEventCount].port = 0;
fMidiEvents[fMidiEventCount].time = static_cast<uint32_t>(vstMidiEvent->deltaFrames);
fMidiEvents[fMidiEventCount].size = 3;

for (uint32_t j=0; j < 3; ++j)
fMidiEvents[fMidiEventCount].data[i] = static_cast<uint8_t>(vstMidiEvent->midiData[i]);

fMidiEventCount += 1;
}
}
break;

case effCanDo:
if (const char* const canDo = (const char*)ptr)
{
if (std::strcmp(canDo, "receiveVstEvents") == 0)
return 1;
if (std::strcmp(canDo, "receiveVstMidiEvent") == 0)
return 1;
if (std::strcmp(canDo, "receiveVstTimeInfo") == 0)
return 1;
}
break;
}

return ret;
}

float vst_getParameter(const int32_t /*index*/)
{
return 0.0f;
}

void vst_setParameter(const int32_t /*index*/, const float /*value*/)
{
}

void vst_processReplacing(const float** const inputs, float** const outputs, const int32_t sampleFrames)
{
if (sampleFrames <= 0)
return;

static const int kWantVstTimeFlags(kVstTransportPlaying|kVstPpqPosValid|kVstTempoValid|kVstBarsValid|kVstTimeSigValid);

if (const VstTimeInfo* const vstTimeInfo = (const VstTimeInfo*)fAudioMaster(fEffect, audioMasterGetTime, 0, kWantVstTimeFlags, nullptr, 0.0f))
{
fTimeInfo.frame = static_cast<uint64_t>(vstTimeInfo->samplePos);
fTimeInfo.playing = (vstTimeInfo->flags & kVstTransportPlaying);
fTimeInfo.bbt.valid = ((vstTimeInfo->flags & kVstTempoValid) != 0 || (vstTimeInfo->flags & kVstTimeSigValid) != 0);

// ticksPerBeat is not possible with VST
fTimeInfo.bbt.ticksPerBeat = 960.0;

if (vstTimeInfo->flags & kVstTempoValid)
fTimeInfo.bbt.beatsPerMinute = vstTimeInfo->tempo;
else
fTimeInfo.bbt.beatsPerMinute = 120.0;

if (vstTimeInfo->flags & kVstTimeSigValid)
{
fTimeInfo.bbt.beatsPerBar = static_cast<float>(vstTimeInfo->timeSigNumerator);
fTimeInfo.bbt.beatType = static_cast<float>(vstTimeInfo->timeSigDenominator);
}
else
{
fTimeInfo.bbt.beatsPerBar = 4.0f;
fTimeInfo.bbt.beatType = 4.0f;
}

if (vstTimeInfo->flags & kVstPpqPosValid)
{
const int ppqPerBar = vstTimeInfo->timeSigNumerator * 4 / vstTimeInfo->timeSigDenominator;
const double barBeats = (std::fmod(vstTimeInfo->ppqPos, ppqPerBar) / ppqPerBar) * vstTimeInfo->timeSigDenominator;
const double rest = std::fmod(barBeats, 1.0);

fTimeInfo.bbt.bar = static_cast<int32_t>(vstTimeInfo->ppqPos)/ppqPerBar + 1;
fTimeInfo.bbt.beat = static_cast<int32_t>(barBeats-rest+1.0);
fTimeInfo.bbt.tick = static_cast<int32_t>(rest*fTimeInfo.bbt.ticksPerBeat+0.5);
}
else
{
fTimeInfo.bbt.bar = 1;
fTimeInfo.bbt.beat = 1;
fTimeInfo.bbt.tick = 0;
}

fTimeInfo.bbt.barStartTick = fTimeInfo.bbt.ticksPerBeat*fTimeInfo.bbt.beatsPerBar*(fTimeInfo.bbt.bar-1);
}

if (fHandle != nullptr)
fDescriptor->process(fHandle, const_cast<float**>(inputs), outputs, static_cast<uint32_t>(sampleFrames), fMidiEvents, fMidiEventCount);

fMidiEventCount = 0;
}

protected:
// -------------------------------------------------------------------

uint32_t handleGetBufferSize() const
{
return fBufferSize;
}

double handleGetSampleRate() const
{
return fSampleRate;
}

bool handleIsOffline() const
{
return false;
}

const NativeTimeInfo* handleGetTimeInfo() const
{
return &fTimeInfo;
}

bool handleWriteMidiEvent(const NativeMidiEvent* const event)
{
CARLA_SAFE_ASSERT_RETURN(fDescriptor->midiOuts > 0, false);
CARLA_SAFE_ASSERT_RETURN(event != nullptr, false);
CARLA_SAFE_ASSERT_RETURN(event->data[0] != 0, false);

// reverse-find first free event, and put it there
for (uint32_t i=(kMaxMidiEvents*2)-1; i > fMidiEventCount; --i)
{
if (fMidiEvents[i].data[0] == 0)
{
std::memcpy(&fMidiEvents[i], event, sizeof(NativeMidiEvent));
return true;
}
}

return false;
}

void handleUiParameterChanged(const uint32_t /*index*/, const float /*value*/) const
{
}

void handleUiCustomDataChanged(const char* const /*key*/, const char* const /*value*/) const
{
}

void handleUiClosed()
{
}

const char* handleUiOpenFile(const bool /*isDir*/, const char* const /*title*/, const char* const /*filter*/) const
{
// TODO
return nullptr;
}

const char* handleUiSaveFile(const bool /*isDir*/, const char* const /*title*/, const char* const /*filter*/) const
{
// TODO
return nullptr;
}

intptr_t handleDispatcher(const NativeHostDispatcherOpcode opcode, const int32_t index, const intptr_t value, void* const ptr, const float opt)
{
carla_debug("NativePlugin::handleDispatcher(%i, %i, " P_INTPTR ", %p, %f)", opcode, index, value, ptr, opt);
return 0;

// unused for now
(void)opcode; (void)index; (void)value; (void)ptr; (void)opt;
}

private:
// VST stuff
const audioMasterCallback fAudioMaster;
AEffect* const fEffect;

// Native data
NativePluginHandle fHandle;
NativeHostDescriptor fHost;
const NativePluginDescriptor* const fDescriptor;

// VST host data
uint32_t fBufferSize;
double fSampleRate;

// Temporary data
uint32_t fMidiEventCount;
NativeMidiEvent fMidiEvents[kMaxMidiEvents];
NativeTimeInfo fTimeInfo;
ERect fVstRect;

char* fStateChunk;

// -------------------------------------------------------------------

#define handlePtr ((NativePlugin*)handle)

static uint32_t host_get_buffer_size(NativeHostHandle handle)
{
return handlePtr->handleGetBufferSize();
}

static double host_get_sample_rate(NativeHostHandle handle)
{
return handlePtr->handleGetSampleRate();
}

static bool host_is_offline(NativeHostHandle handle)
{
return handlePtr->handleIsOffline();
}

static const NativeTimeInfo* host_get_time_info(NativeHostHandle handle)
{
return handlePtr->handleGetTimeInfo();
}

static bool host_write_midi_event(NativeHostHandle handle, const NativeMidiEvent* event)
{
return handlePtr->handleWriteMidiEvent(event);
}

static void host_ui_parameter_changed(NativeHostHandle handle, uint32_t index, float value)
{
handlePtr->handleUiParameterChanged(index, value);
}

static void host_ui_custom_data_changed(NativeHostHandle handle, const char* key, const char* value)
{
handlePtr->handleUiCustomDataChanged(key, value);
}

static void host_ui_closed(NativeHostHandle handle)
{
handlePtr->handleUiClosed();
}

static const char* host_ui_open_file(NativeHostHandle handle, bool isDir, const char* title, const char* filter)
{
return handlePtr->handleUiOpenFile(isDir, title, filter);
}

static const char* host_ui_save_file(NativeHostHandle handle, bool isDir, const char* title, const char* filter)
{
return handlePtr->handleUiSaveFile(isDir, title, filter);
}

static intptr_t host_dispatcher(NativeHostHandle handle, NativeHostDispatcherOpcode opcode, int32_t index, intptr_t value, void* ptr, float opt)
{
return handlePtr->handleDispatcher(opcode, index, value, ptr, opt);
}

#undef handlePtr

CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NativePlugin)
};

// -----------------------------------------------------------------------

#ifdef VESTIGE_HEADER
# define handlePtr ((NativePlugin*)effect->ptr2)
# define validEffect effect != nullptr && effect->ptr2 != nullptr
#else
# define handlePtr ((NativePlugin*)effect->resvd2)
# define validEffect effect != nullptr && effect->resvd2 != 0
#endif

static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t index, intptr_t value, void* ptr, float opt)
{
// handle base opcodes
switch (opcode)
{
case effOpen:
#ifdef VESTIGE_HEADER
if (effect != nullptr && effect->ptr3 != nullptr)
{
audioMasterCallback audioMaster = (audioMasterCallback)effect->ptr3;
#else
if (effect != nullptr && effect->object != nullptr)
{
audioMasterCallback audioMaster = (audioMasterCallback)effect->object;
#endif
d_lastBufferSize = static_cast<uint32_t>(audioMaster(effect, audioMasterGetBlockSize, 0, 0, nullptr, 0.0f));
d_lastSampleRate = static_cast<double>(audioMaster(effect, audioMasterGetSampleRate, 0, 0, nullptr, 0.0f));

// some hosts are not ready at this point or return 0 buffersize/samplerate
if (d_lastBufferSize == 0)
d_lastBufferSize = 2048;
if (d_lastSampleRate <= 0.0)
d_lastSampleRate = 44100.0;

const NativePluginDescriptor* pluginDesc = nullptr;
#ifdef CARLA_PLUGIN_PATCHBAY
const char* const pluginLabel = "carlapatchbay";
#else
const char* const pluginLabel = "carlarack";
#endif

PluginListManager& plm(PluginListManager::getInstance());

for (LinkedList<const NativePluginDescriptor*>::Itenerator it = plm.descs.begin(); it.valid(); it.next())
{
const NativePluginDescriptor* const& tmpDesc(it.getValue());

if (std::strcmp(tmpDesc->label, pluginLabel) == 0)
{
pluginDesc = tmpDesc;
break;
}
}

CARLA_SAFE_ASSERT_RETURN(pluginDesc != nullptr, 0);

NativePlugin* const plugin(new NativePlugin(audioMaster, effect, pluginDesc));
#ifdef VESTIGE_HEADER
effect->ptr2 = plugin;
#else
effect->resvd2 = (intptr_t)plugin;
#endif
return 1;
}
return 0;

case effClose:
if (validEffect)
{
#ifdef VESTIGE_HEADER
delete (NativePlugin*)effect->ptr2;
effect->ptr2 = nullptr;
effect->ptr3 = nullptr;
#else
delete (NativePlugin*)effect->resvd2;
effect->resvd2 = 0;
effect->object = nullptr;
#endif
delete effect;
return 1;
}
return 0;

case effGetPlugCategory:
#ifdef CARLA_PLUGIN_SYNTH
return kPlugCategSynth;
#else
return kPlugCategEffect;
#endif

case effGetEffectName:
if (ptr != nullptr)
{
#ifdef CARLA_PLUGIN_PATCHBAY
std::strncpy((char*)ptr, "Carla-Patchbay", 64);
#else
std::strncpy((char*)ptr, "Carla-Rack", 64);
#endif
return 1;
}
return 0;

case effGetVendorString:
if (ptr != nullptr)
{
std::strncpy((char*)ptr, "falkTX", 64);
return 1;
}
return 0;

case effGetProductString:
if (ptr != nullptr)
{
#ifdef CARLA_PLUGIN_PATCHBAY
std::strncpy((char*)ptr, "CarlaPatchbay", 32);
#else
std::strncpy((char*)ptr, "CarlaRack", 32);
#endif
return 1;
}
return 0;

case effGetVendorVersion:
return CARLA_VERSION_HEX;

case effGetVstVersion:
return kVstVersion;
};

// handle advanced opcodes
if (validEffect)
return handlePtr->vst_dispatcher(opcode, index, value, ptr, opt);

return 0;
}

static float vst_getParameterCallback(AEffect* effect, int32_t index)
{
if (validEffect)
return handlePtr->vst_getParameter(index);
return 0.0f;
}

static void vst_setParameterCallback(AEffect* effect, int32_t index, float value)
{
if (validEffect)
handlePtr->vst_setParameter(index, value);
}

static void vst_processCallback(AEffect* effect, float** inputs, float** outputs, int32_t sampleFrames)
{
if (validEffect)
handlePtr->vst_processReplacing(const_cast<const float**>(inputs), outputs, sampleFrames);
}

static void vst_processReplacingCallback(AEffect* effect, float** inputs, float** outputs, int32_t sampleFrames)
{
if (validEffect)
handlePtr->vst_processReplacing(const_cast<const float**>(inputs), outputs, sampleFrames);
}

#undef handlePtr

// -----------------------------------------------------------------------

CARLA_EXPORT
#ifdef CARLA_OS_WIN
const AEffect* VSTPluginMain(audioMasterCallback audioMaster);
#else
const AEffect* VSTPluginMain(audioMasterCallback audioMaster) asm ("main");
#endif

CARLA_EXPORT
const AEffect* VSTPluginMain(audioMasterCallback audioMaster)
{
// old version
if (audioMaster(nullptr, audioMasterVersion, 0, 0, nullptr, 0.0f) == 0)
return nullptr;

AEffect* const effect(new AEffect);
std::memset(effect, 0, sizeof(AEffect));

// vst fields
effect->magic = kEffectMagic;
effect->uniqueID = CCONST('C', 'r', 'l', 'a');
#ifdef VESTIGE_HEADER
int32_t* const version = (int32_t*)&effect->unknown1;
*version = CARLA_VERSION_HEX;
#else
effect->version = CARLA_VERSION_HEX;
#endif

// plugin fields
effect->numParams = 0;
effect->numPrograms = 0;
effect->numInputs = 2;
effect->numOutputs = 2;

// plugin flags
effect->flags |= effFlagsCanReplacing;
effect->flags |= effFlagsHasEditor;
effect->flags |= effFlagsProgramChunks;
#ifdef CARLA_PLUGIN_SYNTH
effect->flags |= effFlagsIsSynth;
#endif

// static calls
effect->dispatcher = vst_dispatcherCallback;
effect->process = vst_processCallback;
effect->getParameter = vst_getParameterCallback;
effect->setParameter = vst_setParameterCallback;
effect->processReplacing = vst_processReplacingCallback;

// pointers
#ifdef VESTIGE_HEADER
effect->ptr3 = (void*)audioMaster;
#else
effect->object = (void*)audioMaster;
#endif

return effect;
}

// -----------------------------------------------------------------------

Loading…
Cancel
Save