Browse Source

More work for carla-lv2-single, can export basic fx now

tags/1.9.8
falkTX 7 years ago
parent
commit
050414c63d
4 changed files with 231 additions and 6 deletions
  1. +194
    -2
      source/backend/plugin/CarlaPlugin.cpp
  2. +19
    -0
      source/carla_skin.py
  3. +2
    -0
      source/libjack/libjack.cpp
  4. +16
    -4
      source/plugin/carla-lv2-single.cpp

+ 194
- 2
source/backend/plugin/CarlaPlugin.cpp View File

@@ -30,6 +30,7 @@
using juce::CharPointer_UTF8; using juce::CharPointer_UTF8;
using juce::File; using juce::File;
using juce::MemoryOutputStream; using juce::MemoryOutputStream;
using juce::Result;
using juce::ScopedPointer; using juce::ScopedPointer;
using juce::String; using juce::String;
using juce::XmlDocument; using juce::XmlDocument;
@@ -922,9 +923,200 @@ bool CarlaPlugin::exportAsLV2(const char* const lv2path)
CARLA_SAFE_ASSERT_RETURN(lv2path != nullptr && lv2path[0] != '\0', false); CARLA_SAFE_ASSERT_RETURN(lv2path != nullptr && lv2path[0] != '\0', false);
carla_debug("CarlaPlugin::exportAsLV2(\"%s\")", lv2path); carla_debug("CarlaPlugin::exportAsLV2(\"%s\")", lv2path);


// TODO
CarlaString bundlepath(lv2path);


return false;
if (! bundlepath.endsWith(".lv2"))
bundlepath += ".lv2";

const File bundlefolder(bundlepath.buffer());

if (bundlefolder.existsAsFile())
{
pData->engine->setLastError("Requested filename already exists as file, use a folder instead");
return false;
}

if (! bundlefolder.exists())
{
const Result res(bundlefolder.createDirectory());

if (res.failed())
{
pData->engine->setLastError(res.getErrorMessage().toRawUTF8());
return false;
}
}

CarlaString symbol(pData->name);
symbol.toBasic();

char strBufName[STR_MAX+1];
char strBufSymbol[STR_MAX+1];
strBufName[STR_MAX] = strBufSymbol[STR_MAX] = '\0';

{
const CarlaString pluginFilename(bundlepath + CARLA_OS_SEP_STR + symbol + ".xml");

if (! saveStateToFile(pluginFilename))
return false;
}

{
MemoryOutputStream manifestStream;

manifestStream << "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n";
manifestStream << "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n";
manifestStream << "\n";
manifestStream << "<" << symbol.buffer() << ".ttl>\n";
manifestStream << " a lv2:Plugin ;\n";
manifestStream << " lv2:binary <" << symbol.buffer() << ".so> ;\n";
manifestStream << " rdfs:seeAlso <" << symbol.buffer() << ".ttl> .\n";

const CarlaString manifestFilename(bundlepath + CARLA_OS_SEP_STR "manifest.ttl");
const File manifestFile(manifestFilename.buffer());

if (! manifestFile.replaceWithData(manifestStream.getData(), manifestStream.getDataSize()))
{
pData->engine->setLastError("Failed to write manifest.ttl file");
return false;
}
}

{
MemoryOutputStream mainStream;

mainStream << "@prefix doap: <http://usefulinc.com/ns/doap#> .\n";
mainStream << "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n";
mainStream << "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n";
mainStream << "\n";
mainStream << "<>\n";
mainStream << " a lv2:Plugin ;\n";
mainStream << "\n";
mainStream << " lv2:requiredFeature <http://lv2plug.in/ns/ext/buf-size#boundedBlockLength> ,\n";
mainStream << " <http://lv2plug.in/ns/ext/options#options> ,\n";
mainStream << " <http://lv2plug.in/ns/ext/urid#map> ;\n";
mainStream << "\n";

int portIndex = 0;

for (uint32_t i=0; i<pData->audioIn.count; ++i)
{
const String portIndexNum(portIndex++);
const String portIndexLabel(portIndex);

mainStream << " lv2:port [\n";
mainStream << " a lv2:InputPort, lv2:AudioPort ;\n";
mainStream << " lv2:index " << portIndexNum << " ;\n";
mainStream << " lv2:symbol \"lv2_audio_in_" << portIndexLabel << "\" ;\n";
mainStream << " lv2:name \"Audio Input " << portIndexLabel << "\" ;\n";
mainStream << " ] ;\n";
}

for (uint32_t i=0; i<pData->audioOut.count; ++i)
{
const String portIndexNum(portIndex++);
const String portIndexLabel(portIndex);

mainStream << " lv2:port [\n";
mainStream << " a lv2:OutputPort, lv2:AudioPort ;\n";
mainStream << " lv2:index " << portIndexNum << " ;\n";
mainStream << " lv2:symbol \"lv2_audio_out_" << portIndexLabel << "\" ;\n";
mainStream << " lv2:name \"Audio Output " << portIndexLabel << "\" ;\n";
mainStream << " ] ;\n";
}

mainStream << " lv2:port [\n";
mainStream << " a lv2:InputPort, lv2:ControlPort ;\n";
mainStream << " lv2:index " << String(portIndex++) << " ;\n";
mainStream << " lv2:name \"freewheel\" ;\n";
mainStream << " lv2:symbol \"freewheel\" ;\n";
mainStream << " lv2:default 0 ;\n";
mainStream << " lv2:minimum 0 ;\n";
mainStream << " lv2:maximum 1 ;\n";
mainStream << " lv2:portProperty lv2:toggled , lv2:integer;\n";
// TODO designation, hidegui
mainStream << " ] ;\n";

for (uint32_t i=0; i<pData->param.count; ++i)
{
const ParameterData& paramData(pData->param.data[i]);
const ParameterRanges& paramRanges(pData->param.ranges[i]);

const String portIndexNum(portIndex++);
const String portIndexLabel(portIndex);

mainStream << " lv2:port [\n";

if (paramData.type == PARAMETER_INPUT)
mainStream << " a lv2:InputPort, lv2:ControlPort ;\n";
else
mainStream << " a lv2:OutputPort, lv2:ControlPort ;\n";

if (paramData.hints & PARAMETER_IS_BOOLEAN)
mainStream << " lv2:portProperty lv2:toggled ;\n";

if (paramData.hints & PARAMETER_IS_INTEGER)
mainStream << " lv2:portProperty lv2:integer ;\n";

// TODO logarithmic, enabled (not on gui), automable, samplerate, scalepoints

strBufName[0] = strBufSymbol[0] = '\0';
getParameterName(i, strBufName);
getParameterSymbol(i, strBufSymbol);

if (strBufSymbol[0] == '\0')
{
CarlaString s(strBufName);
s.toBasic();
std::memcpy(strBufSymbol, s.buffer(), s.length()+1);
}

mainStream << " lv2:index " << portIndexNum << " ;\n";
mainStream << " lv2:symbol \"" << strBufSymbol << "\" ;\n";
mainStream << " lv2:name \"\"\"" << strBufName << "\"\"\" ;\n";
mainStream << " lv2:default " << String(paramRanges.def) << " ;\n";
mainStream << " lv2:minimum " << String(paramRanges.min) << " ;\n";
mainStream << " lv2:maximum " << String(paramRanges.max) << " ;\n";

// TODO midiCC, midiChannel

mainStream << " ] ;\n";
}

mainStream << " rdfs:comment \"Plugin generated using Carla LV2 export.\" ;\n";
mainStream << " doap:name \"\"\"" << getName() << "\"\"\" .\n";
mainStream << "\n";

const CarlaString mainFilename(bundlepath + CARLA_OS_SEP_STR + symbol + ".ttl");
const File mainFile(mainFilename.buffer());

if (! mainFile.replaceWithData(mainStream.getData(), mainStream.getDataSize()))
{
pData->engine->setLastError("Failed to write main plugin ttl file");
return false;
}
}

const CarlaString binaryFilename(bundlepath + CARLA_OS_SEP_STR + symbol + ".so");

const File binaryFileSource(File::getSpecialLocation(File::currentExecutableFile).getSiblingFile("carla-lv2-single.so"));
const File binaryFileTarget(binaryFilename.buffer());

if (! binaryFileSource.createSymbolicLink(binaryFileTarget, true))
{
pData->engine->setLastError("Failed to create symbolik link of plugin binary");
return false;
}

const EngineOptions& opts(pData->engine->getOptions());

const CarlaString binFolderTarget(bundlepath + CARLA_OS_SEP_STR + "bin");
const CarlaString resFolderTarget(bundlepath + CARLA_OS_SEP_STR + "res");

File(opts.binaryDir).createSymbolicLink(File(binFolderTarget.buffer()), true);
File(opts.resourceDir).createSymbolicLink(File(resFolderTarget.buffer()), true);

return true;
} }


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


+ 19
- 0
source/carla_skin.py View File

@@ -1040,6 +1040,10 @@ class AbstractPluginSlot(QFrame, PluginEditParentMeta):
if self.fIdleTimerId != 0: if self.fIdleTimerId != 0:
actRemove.setVisible(False) actRemove.setVisible(False)


menu.addSeparator()

actExportLV2 = menu.addAction(self.tr("Export LV2..."))

# ------------------------------------------------------------- # -------------------------------------------------------------
# exec # exec


@@ -1113,6 +1117,21 @@ class AbstractPluginSlot(QFrame, PluginEditParentMeta):
self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok) self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)


# ------------------------------------------------------------- # -------------------------------------------------------------
# Export LV2

elif actSel == actExportLV2:
ret = QFileDialog.getSaveFileName(self, self.tr("Export Plugin as LV2"), "", "", QFileDialog.ShowDirsOnly|QFileDialog.HideNameFilterDetails)

if config_UseQt5:
ret = ret[0]
if not ret:
return

if not self.host.export_plugin_lv2(self.fPluginId, ret):
CustomMessageBox(self, QMessageBox.Warning, self.tr("Error"), self.tr("Operation failed"),
self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)

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


@pyqtSlot() @pyqtSlot()
def slot_knobCustomMenu(self): def slot_knobCustomMenu(self):


+ 2
- 0
source/libjack/libjack.cpp View File

@@ -853,6 +853,7 @@ void CarlaJackAppClient::runNonRealtimeThread()
if (quitReceived) if (quitReceived)
break; break;


/*
if (fLastPingTime > 0 && Time::currentTimeMillis() > fLastPingTime + 30000) if (fLastPingTime > 0 && Time::currentTimeMillis() > fLastPingTime + 30000)
{ {
carla_stderr("Did not receive ping message from server for 30 secs, closing..."); carla_stderr("Did not receive ping message from server for 30 secs, closing...");
@@ -861,6 +862,7 @@ void CarlaJackAppClient::runNonRealtimeThread()
fRealtimeThread.signalThreadShouldExit(); fRealtimeThread.signalThreadShouldExit();
break; break;
} }
*/
} }


//callback(ENGINE_CALLBACK_ENGINE_STOPPED, 0, 0, 0, 0.0f, nullptr); //callback(ENGINE_CALLBACK_ENGINE_STOPPED, 0, 0, 0, 0.0f, nullptr);


+ 16
- 4
source/plugin/carla-lv2-single.cpp View File

@@ -38,8 +38,11 @@ public:
fIsOffline(false) fIsOffline(false)
{ {
// xxxxx // xxxxx
CarlaString binaryDir(bundlePath);
binaryDir += CARLA_OS_SEP_STR "bin" CARLA_OS_SEP_STR;

CarlaString resourceDir(bundlePath); CarlaString resourceDir(bundlePath);
resourceDir += CARLA_OS_SEP_STR "resources" CARLA_OS_SEP_STR;
resourceDir += CARLA_OS_SEP_STR "res" CARLA_OS_SEP_STR;


pData->bufferSize = bufferSize; pData->bufferSize = bufferSize;
pData->sampleRate = sampleRate; pData->sampleRate = sampleRate;
@@ -57,12 +60,15 @@ public:
if (pData->options.binaryDir != nullptr) if (pData->options.binaryDir != nullptr)
delete[] pData->options.binaryDir; delete[] pData->options.binaryDir;


pData->options.binaryDir = binaryDir.dup();
pData->options.resourceDir = resourceDir.dup(); pData->options.resourceDir = resourceDir.dup();
pData->options.binaryDir = carla_strdup(carla_get_library_folder());


setCallback(_engine_callback, this); setCallback(_engine_callback, this);


if (! addPlugin(BINARY_NATIVE, PLUGIN_VST2, "/usr/lib/vst/3BandEQ-vst.so", nullptr, nullptr, 0, nullptr, 0x0))
using juce::File;
const File pluginFile(File::getSpecialLocation(File::currentExecutableFile).withFileExtension("xml"));

if (! loadProject(pluginFile.getFullPathName().toRawUTF8()))
{ {
carla_stderr2("Failed to init plugin, possible reasons: %s", getLastError()); carla_stderr2("Failed to init plugin, possible reasons: %s", getLastError());
return; return;
@@ -462,7 +468,13 @@ static LV2_Handle lv2_instantiate(const LV2_Descriptor* lv2Descriptor, double sa
return nullptr; return nullptr;
} }


return new CarlaEngineLV2Single(bufferSize, sampleRate, bundlePath, uridMap);
CarlaEngineLV2Single* const instance(new CarlaEngineLV2Single(bufferSize, sampleRate, bundlePath, uridMap));

if (instance->hasPlugin())
return (LV2_Handle)instance;

delete instance;
return nullptr;
} }


#define instancePtr ((CarlaEngineLV2Single*)instance) #define instancePtr ((CarlaEngineLV2Single*)instance)


Loading…
Cancel
Save