Browse Source

Experimental inline display inside canvas

Signed-off-by: falkTX <falktx@falktx.com>
tags/v2.1-rc1
falkTX 6 years ago
parent
commit
b7922f3dd5
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
9 changed files with 112 additions and 9 deletions
  1. +7
    -1
      source/backend/CarlaBackend.h
  2. +26
    -3
      source/backend/plugin/CarlaPluginLV2.cpp
  3. +20
    -1
      source/frontend/carla_backend.py
  4. +1
    -0
      source/frontend/carla_backend_qt.py
  5. +17
    -1
      source/frontend/carla_host.py
  6. +1
    -0
      source/frontend/patchcanvas/__init__.py
  7. +26
    -3
      source/frontend/patchcanvas/canvasbox.py
  8. +12
    -0
      source/frontend/patchcanvas/patchcanvas.py
  9. +2
    -0
      source/utils/CarlaBackendUtils.hpp

+ 7
- 1
source/backend/CarlaBackend.h View File

@@ -988,7 +988,13 @@ typedef enum {
/*!
* The engine has crashed or malfunctioned and will no longer work.
*/
ENGINE_CALLBACK_QUIT = 41
ENGINE_CALLBACK_QUIT = 41,

/*!
* A plugin requested for its inline display to be redrawn.
* @a pluginId Plugin Id to redraw
*/
ENGINE_CALLBACK_INLINE_DISPLAY_REDRAW = 42,

} EngineCallbackOpcode;



+ 26
- 3
source/backend/plugin/CarlaPluginLV2.cpp View File

@@ -37,6 +37,7 @@ extern "C" {
}

#include "water/files/File.h"
#include "water/misc/Time.h"

#include <string>
#include <vector>
@@ -528,6 +529,8 @@ public:
fParamBuffers(nullptr),
fNeedsFixedBuffers(false),
fNeedsUiClose(false),
fInlineDisplayNeedsRedraw(false),
fInlineDisplayLastRedrawTime(0),
fLatencyIndex(-1),
fStrictBounds(-1),
fAtomBufferEvIn(),
@@ -1689,6 +1692,21 @@ public:
}
}

if (fInlineDisplayNeedsRedraw)
{
const int64_t timeNow = water::Time::currentTimeMillis();

if (timeNow - fInlineDisplayLastRedrawTime > (1000 / 30))
{
fInlineDisplayNeedsRedraw = false;
fInlineDisplayLastRedrawTime = timeNow;
pData->engine->callback(true, true,
ENGINE_CALLBACK_INLINE_DISPLAY_REDRAW,
pData->id,
0, 0, 0, 0.0f, nullptr);
}
}

CarlaPlugin::idle();
}

@@ -2698,7 +2716,7 @@ public:
pData->options &= ~PLUGIN_OPTION_FORCE_STEREO;

// plugin hints
pData->hints = 0x0;
pData->hints = (pData->hints & PLUGIN_HAS_INLINE_DISPLAY) ? PLUGIN_HAS_INLINE_DISPLAY : 0;

if (isRealtimeSafe())
pData->hints |= PLUGIN_IS_RTSAFE;
@@ -4677,8 +4695,11 @@ public:
if (pData->hints & PLUGIN_HAS_EXTENSION_WORKER)
fExt.worker = (const LV2_Worker_Interface*)fDescriptor->extension_data(LV2_WORKER__interface);

if (pData->hints & PLUGIN_HAS_EXTENSION_INLINE_DISPLAY)
// FIXME
// if (pData->hints & PLUGIN_HAS_EXTENSION_INLINE_DISPLAY)
{
fExt.inlineDisplay = (const LV2_Inline_Display_Interface*)fDescriptor->extension_data(LV2_INLINEDISPLAY__interface);
}

// check if invalid
if (fExt.options != nullptr && fExt.options->get == nullptr && fExt.options->set == nullptr)
@@ -4997,7 +5018,7 @@ public:

void handleInlineDisplayQueueRedraw()
{
// TODO
fInlineDisplayNeedsRedraw = true;
}

LV2_Inline_Display_Image_Surface* renderInlineDisplay(int width, int height)
@@ -6121,6 +6142,8 @@ private:

bool fNeedsFixedBuffers;
bool fNeedsUiClose;
bool fInlineDisplayNeedsRedraw;
int64_t fInlineDisplayLastRedrawTime;
int32_t fLatencyIndex; // -1 if invalid
int fStrictBounds; // -1 unsupported, 0 optional, 1 required



+ 20
- 1
source/frontend/carla_backend.py View File

@@ -22,6 +22,8 @@
from abc import ABCMeta, abstractmethod
from ctypes import *
from platform import architecture
from sip import voidptr
from struct import pack
from sys import platform, maxsize

# ------------------------------------------------------------------------------------------------------------
@@ -726,6 +728,10 @@ ENGINE_CALLBACK_ERROR = 40
# The engine has crashed or malfunctioned and will no longer work.
ENGINE_CALLBACK_QUIT = 41

# A plugin requested for its inline display to be redrawn.
# @a pluginId Plugin Id to redraw
ENGINE_CALLBACK_INLINE_DISPLAY_REDRAW = 42

# ------------------------------------------------------------------------------------------------------------
# NSM Callback Opcode
# NSM callback opcodes.
@@ -2818,7 +2824,20 @@ class CarlaHostDLL(CarlaHostMeta):
return float(self.lib.carla_get_output_peak_value(pluginId, isLeft))

def render_inline_display(self, pluginId, width, height):
return structToDict(self.lib.carla_render_inline_display(pluginId, width, height))
ptr = self.lib.carla_render_inline_display(pluginId, width, height)
if not ptr or not ptr.contents:
return None
contents = ptr.contents
datalen = contents.height * contents.stride
unpacked = tuple(contents.data[i] for i in range(datalen))
packed = pack("%iB" % datalen, *unpacked)
data = {
'data': voidptr(packed),
'width': contents.width,
'height': contents.height,
'stride': contents.stride,
}
return data

def set_option(self, pluginId, option, yesNo):
self.lib.carla_set_option(pluginId, option, yesNo)


+ 1
- 0
source/frontend/carla_backend_qt.py View File

@@ -72,6 +72,7 @@ class CarlaHostSignals(QObject):
InfoCallback = pyqtSignal(str)
ErrorCallback = pyqtSignal(str)
QuitCallback = pyqtSignal()
InlineDisplayRedrawCallback = pyqtSignal(int)

# ------------------------------------------------------------------------------------------------------------
# Carla Host object (dummy/null, does nothing)


+ 17
- 1
source/frontend/carla_host.py View File

@@ -526,6 +526,7 @@ class HostWindow(QMainWindow):
host.InfoCallback.connect(self.slot_handleInfoCallback)
host.ErrorCallback.connect(self.slot_handleErrorCallback)
host.QuitCallback.connect(self.slot_handleQuitCallback)
host.InlineDisplayRedrawCallback.connect(self.slot_handleInlineDisplayRedrawCallback)

# ----------------------------------------------------------------------------------------------------
# Final setup
@@ -1550,7 +1551,7 @@ class HostWindow(QMainWindow):

if pluginId == MAIN_CARLA_PLUGIN_ID:
hasCustomUI = False
hasInlineDisplay = False
hasInlineDisplay = 69 #False
else:
hints = self.host.get_plugin_info(pluginId)['hints']
hasCustomUI = bool(hints & PLUGIN_HAS_CUSTOM_UI)
@@ -2199,6 +2200,10 @@ class HostWindow(QMainWindow):
self.removeAllPlugins()
self.projectLoadingFinished()

@pyqtSlot(int)
def slot_handleInlineDisplayRedrawCallback(self, pluginId):
patchcanvas.redrawPluginGroup(pluginId)

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

@pyqtSlot()
@@ -2597,6 +2602,13 @@ def canvasCallback(action, value1, value2, valueStr):
elif action == patchcanvas.ACTION_BG_RIGHT_CLICK:
gCarla.gui.showPluginActionsMenu()

elif action == patchcanvas.ACTION_INLINE_DISPLAY:
# FIXME
if gCarla.gui.fPluginCount == 0: return
pluginId = value1
width, height = [int(v) for v in valueStr.split(":")]
return host.render_inline_display(pluginId, width, height)

# ------------------------------------------------------------------------------------------------------------
# Engine callback

@@ -2700,6 +2712,10 @@ def engineCallback(host, action, pluginId, value1, value2, value3, valuef, value
host.ErrorCallback.emit(valueStr)
elif action == ENGINE_CALLBACK_QUIT:
host.QuitCallback.emit()
elif action == ENGINE_CALLBACK_INLINE_DISPLAY_REDRAW:
host.InlineDisplayRedrawCallback.emit(pluginId)
else:
print("unhandled action", action)

# ------------------------------------------------------------------------------------------------------------
# File callback


+ 1
- 0
source/frontend/patchcanvas/__init__.py View File

@@ -60,6 +60,7 @@ ACTION_PLUGIN_REPLACE = 11 # plugin_id, N, N
ACTION_PLUGIN_REMOVE = 12 # plugin_id, N, N
ACTION_PLUGIN_SHOW_UI = 13 # plugin_id, N, N
ACTION_BG_RIGHT_CLICK = 14 # N, N, N
ACTION_INLINE_DISPLAY = 15 # plugin_id, N, N

# Icon
ICON_APPLICATION = 0


+ 26
- 3
source/frontend/patchcanvas/canvasbox.py View File

@@ -20,7 +20,7 @@
# Imports (Global)

from PyQt5.QtCore import qCritical, Qt, QPointF, QRectF, QTimer
from PyQt5.QtGui import QCursor, QFont, QFontMetrics, QLinearGradient, QPainter, QPen
from PyQt5.QtGui import QCursor, QFont, QFontMetrics, QImage, QLinearGradient, QPainter, QPen
from PyQt5.QtWidgets import QGraphicsItem, QMenu

# ------------------------------------------------------------------------------------------------------------
@@ -44,6 +44,7 @@ from . import (
ACTION_GROUP_SPLIT,
ACTION_GROUP_RENAME,
ACTION_PORTS_DISCONNECT,
ACTION_INLINE_DISPLAY,
EYECANDY_FULL,
PORT_MODE_NULL,
PORT_MODE_INPUT,
@@ -87,6 +88,8 @@ class CanvasBox(QGraphicsItem):

# Base Variables
self.p_width = 50
self.p_width_in = 0
self.p_width_out = 0
self.p_height = canvas.theme.box_header_height + canvas.theme.box_header_spacing + 1

self.m_last_pos = QPointF()
@@ -259,7 +262,7 @@ class CanvasBox(QGraphicsItem):

# Check Text Name size
app_name_size = QFontMetrics(self.m_font_name).width(self.m_group_name) + 30
self.p_width = max(50, app_name_size)
self.p_width = max(200 if self.m_plugin_inline else 50, app_name_size)

# Get Port List
port_list = []
@@ -269,6 +272,8 @@ class CanvasBox(QGraphicsItem):

if len(port_list) == 0:
self.p_height = canvas.theme.box_header_height
self.p_width_in = 0
self.p_width_out = 0
else:
max_in_width = max_out_width = 0
port_spacing = canvas.theme.port_height + canvas.theme.port_spacing
@@ -303,7 +308,12 @@ class CanvasBox(QGraphicsItem):
port.widget.setY(last_out_pos)
last_out_pos += port_spacing

self.p_width = max(self.p_width, 30 + max_in_width + max_out_width)
self.p_width = max(self.p_width, (100 if self.m_plugin_inline else 30) + max_in_width + max_out_width)
self.p_width_in = max_in_width
self.p_width_out = max_out_width

#if self.m_plugin_inline:
#self.p_width += 10

# Horizontal ports re-positioning
inX = canvas.theme.port_offset
@@ -609,6 +619,19 @@ class CanvasBox(QGraphicsItem):
rect.adjust(lineHinting, lineHinting, -lineHinting, -lineHinting)
painter.drawRect(rect)

# Draw plugin inline display if supported
if self.m_plugin_id >= 0 and self.m_plugin_id <= MAX_PLUGIN_ID_ALLOWED and self.m_plugin_inline:
size = "%i:%i" % (self.p_width - self.p_width_in - self.p_width_out - 16,
self.p_height - canvas.theme.box_header_height)
data = canvas.callback(ACTION_INLINE_DISPLAY, self.m_plugin_id, 0, size)
if data is not None:
image = QImage(data['data'], data['width'], data['height'], data['stride'], QImage.Format_ARGB32)
painter.drawImage(self.p_width_in + 7,
canvas.theme.box_header_height
+ (self.p_height - canvas.theme.box_header_height) / 2
- data['height'] / 2 - 1,
image)

# Draw pixmap header
rect.setHeight(canvas.theme.box_header_height)
if canvas.theme.box_header_pixmap:


+ 12
- 0
source/frontend/patchcanvas/patchcanvas.py View File

@@ -690,6 +690,18 @@ def setGroupAsPlugin(group_id, plugin_id, hasUI, hasInlineDisplay):
qCritical("PatchCanvas::setGroupAsPlugin(%i, %i, %s, %s) - unable to find group to set as plugin" % (
group_id, plugin_id, bool2str(hasUI), bool2str(hasInlineDisplay)))

def redrawPluginGroup(plugin_id):
for group in canvas.group_list:
if group.plugin_id == plugin_id:
group.widgets[0].update()

if group.split and group.widgets[1]:
group.widgets[1].update()

return

qCritical("PatchCanvas::redrawPluginGroup(%i) - unable to find group" % plugin_id)

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

def addPort(group_id, port_id, port_name, port_mode, port_type, is_alternate=False):


+ 2
- 0
source/utils/CarlaBackendUtils.hpp View File

@@ -284,6 +284,8 @@ const char* EngineCallbackOpcode2Str(const EngineCallbackOpcode opcode) noexcept
return "ENGINE_CALLBACK_ERROR";
case ENGINE_CALLBACK_QUIT:
return "ENGINE_CALLBACK_QUIT";
case ENGINE_CALLBACK_INLINE_DISPLAY_REDRAW:
return "ENGINE_CALLBACK_INLINE_DISPLAY_REDRAW";
}

carla_stderr("CarlaBackend::EngineCallbackOpcode2Str(%i) - invalid opcode", opcode);


Loading…
Cancel
Save