Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add automation support for Carla. #4569

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 161 additions & 11 deletions plugins/carlabase/carla.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,20 @@
#include "InstrumentPlayHandle.h"
#include "InstrumentTrack.h"
#include "Mixer.h"
#include "Knob.h"
#include "EffectControls.h"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you will not need to include EffectControls.h here as well as in carla.h.


#include <QApplication>
#include <QFileDialog>
#include <QFileInfo>
#include <QPushButton>
#include <QTimerEvent>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QSizePolicy>
#include <QScrollArea>
#include <QtDebug>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend you including <QDebug> instead which does the same thing.

#include <QString>

#include <cstring>

Expand Down Expand Up @@ -299,14 +306,69 @@ void CarlaInstrument::saveSettings(QDomDocument& doc, QDomElement& parent)
return;

QDomDocument carlaDoc("carla");

if (carlaDoc.setContent(QString(state)))
{
QDomNode n = doc.importNode(carlaDoc.documentElement(), true);
parent.appendChild(n);
}

std::free(state);

for (uint32_t i=0; i < floatModels.count(); ++i)
{
QString idStr = CARLA_SETTING_PREFIX + QString::number(i);
floatModels[i]->saveSettings( doc, parent, idStr );
}

}

void CarlaInstrument::refreshParams()
{
if (fDescriptor->get_parameter_count != nullptr &&
fDescriptor->get_parameter_info != nullptr &&
fDescriptor->get_parameter_value != nullptr &&
fDescriptor->set_parameter_value != nullptr)
{
uint32_t param_count = fDescriptor->get_parameter_count(fHandle);
if (param_count > 0)
{
clearKnobModels();
floatModels.reserve(param_count);
for (uint32_t i=0; i < param_count; ++i)
{
// https://github.com/falkTX/Carla/tree/master/source/native-plugins source/native-plugins/resources/carla-plugin
const NativeParameter* paramInfo(fDescriptor->get_parameter_info(fHandle, i));
float param_value = fDescriptor->get_parameter_value(fHandle, i);

// Get parameter name
QString name = "_NO_NAME_";
if (paramInfo->name != nullptr){
name = paramInfo->name;
}

// Create new model for the knob.
floatModels.push_back(new FloatModel(param_value,0.0f,1.0f,0.001f,this,name));

// Load settings into model.
QString idStr = CARLA_SETTING_PREFIX + QString::number(i);
floatModels[i]->loadSettings( settingsElem, idStr );

// Connect to signal dataChanged to knobChanged function.
connect( floatModels[i], &FloatModel::dataChanged, [=]() { knobModelChanged(i); });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Connecting slots to functors is not supported in Qt4. You have two choices:

  • Use normal SIGNAL()/SLOT() and use sender() to detect the sender(what we already do for VSTs)
  • Keep this syntax and switch to master branch which has dropped the support for Qt4

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think you should use Qt::DirectConnection because automated changes will be handled in the UI thread if you use the normal connection type. It may prevent automation working correctly when exporting.


// TODO Signal view to update?
}
}
}
}

void CarlaInstrument::clearKnobModels(){ floatModels.clear(); }

void CarlaInstrument::knobModelChanged(uint32_t index)
{
float value = floatModels[index]->value();
if (fDescriptor->set_parameter_value != nullptr){
fDescriptor->set_parameter_value(fHandle, index, value);
}
}

void CarlaInstrument::loadSettings(const QDomElement& elem)
Expand All @@ -318,6 +380,10 @@ void CarlaInstrument::loadSettings(const QDomElement& elem)
carlaDoc.appendChild(carlaDoc.importNode(elem.firstChildElement(), true ));

fDescriptor->set_state(fHandle, carlaDoc.toString(0).toUtf8().constData());

// Store to load parameter knobs settings when added.
settingsElem = const_cast<QDomElement&>(elem);
refreshParams();
}

void CarlaInstrument::play(sampleFrame* workingBuffer)
Expand Down Expand Up @@ -473,18 +539,27 @@ CarlaInstrumentView::CarlaInstrumentView(CarlaInstrument* const instrument, QWid
: InstrumentView(instrument, parent),
fHandle(instrument->fHandle),
fDescriptor(instrument->fDescriptor),
fTimerId(fHandle != NULL && fDescriptor->ui_idle != NULL ? startTimer(30) : 0)
fTimerId(fHandle != NULL && fDescriptor->ui_idle != NULL ? startTimer(30) : 0),
p_instrument(instrument)
{
lMaxColumns = 4;
lCurColumn = 0;
lCurRow = 0;
setAutoFillBackground(true);

QPalette pal;
pal.setBrush(backgroundRole(), instrument->kIsPatchbay ? PLUGIN_NAME::getIconPixmap("artwork-patchbay") : PLUGIN_NAME::getIconPixmap("artwork-rack"));
setPalette(pal);

QVBoxLayout * l = new QVBoxLayout( this );
l->setContentsMargins( 20, 180, 10, 10 );
l->setSpacing( 10 );
l->setContentsMargins( 3, 40, 3, 3 );
l->setSpacing( 3 );
l->setAlignment( Qt::AlignTop );

// Horizontal layout for the buttons
QHBoxLayout * hl = new QHBoxLayout(this);

// Show GUI button
m_toggleUIButton = new QPushButton( tr( "Show GUI" ), this );
m_toggleUIButton->setCheckable( true );
m_toggleUIButton->setChecked( false );
Expand All @@ -495,10 +570,36 @@ CarlaInstrumentView::CarlaInstrumentView(CarlaInstrument* const instrument, QWid
m_toggleUIButton->setWhatsThis(
tr( "Click here to show or hide the graphical user interface (GUI) of Carla." ) );

l->addWidget( m_toggleUIButton );
l->addStretch();

// Refresh params button
m_refreshParamsButton = new QPushButton( tr( "" ), this );
m_refreshParamsButton->setIcon( embed::getIconPixmap( "reload" ) );
m_refreshParamsButton->setWhatsThis(
tr( "Click here to reload the Carla parameter knobs." ) );
QSizePolicy sizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
sizePolicy.setHorizontalStretch(0);
sizePolicy.setVerticalStretch(0);
sizePolicy.setHeightForWidth(m_refreshParamsButton->sizePolicy().hasHeightForWidth());
m_refreshParamsButton->setSizePolicy(sizePolicy);
m_refreshParamsButton->setCheckable( false );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mixing old and new coding styles doesn't look good. I suggest putting whitespaces either anywhere or nowhere.


// ScrollArea that will contain the knobs
m_scrollArea = new QScrollArea(this);
QWidget * scrollAreaWidget = new QWidget( this );
m_scrollAreaLayout = new QGridLayout( scrollAreaWidget );
scrollAreaWidget->setLayout( m_scrollAreaLayout );
m_scrollArea->setWidget( scrollAreaWidget );
m_scrollArea->setWidgetResizable( true );

// Add widgets to layout
hl->addWidget( m_toggleUIButton );
hl->addWidget( m_refreshParamsButton );
l->addLayout( hl );
l->addWidget( m_scrollArea );

connect( m_refreshParamsButton, SIGNAL( clicked(bool) ), this, SLOT( onRefreshButton() ) );
connect(instrument, SIGNAL(uiClosed()), this, SLOT(uiClosed()));

refreshButtons();
}

CarlaInstrumentView::~CarlaInstrumentView()
Expand All @@ -507,6 +608,57 @@ CarlaInstrumentView::~CarlaInstrumentView()
toggleUI(false);
}

void CarlaInstrumentView::addKnob(FloatModel& knobModel){
Knob* new_knob = new Knob(this);

QString name = knobModel.displayName();
new_knob->setHintText(name,"");
new_knob->setLabel(name);

// Set the newly created model to the knob.
new_knob->setModel(&knobModel);

// Add the new knob to layout
m_scrollAreaLayout->addWidget(new_knob, lCurRow, lCurColumn);

if (lCurColumn < lMaxColumns-1){
lCurColumn++;
} else {
lCurColumn=0;
lCurRow++;
}
}
void CarlaInstrumentView::clearKnobs(){
QLayoutItem *item;
while((item = m_scrollAreaLayout->takeAt(0))) {
if (item->widget()) {
delete item->widget();
}
delete item;
}
lCurColumn = 0;
lCurRow = 0;
}

void CarlaInstrumentView::onRefreshButton(){
p_instrument->refreshParams();
refreshButtons();
}

void CarlaInstrumentView::refreshButtons()
{
CarlaInstrument * instrument = castModel<CarlaInstrument>();
if (instrument->floatModels.count()==0){return;}
if (instrument->floatModels.count() != m_scrollAreaLayout->count()){
clearKnobs();

for (uint32_t i=0; i < instrument->floatModels.count(); ++i)
{
addKnob(*instrument->floatModels[i]);
}
}
}

void CarlaInstrumentView::toggleUI(bool visible)
{
if (fHandle != NULL && fDescriptor->ui_show != NULL)
Expand All @@ -520,6 +672,7 @@ void CarlaInstrumentView::uiClosed()

void CarlaInstrumentView::modelChanged()
{
refreshButtons();
}

void CarlaInstrumentView::timerEvent(QTimerEvent* event)
Expand All @@ -529,6 +682,3 @@ void CarlaInstrumentView::timerEvent(QTimerEvent* event)

InstrumentView::timerEvent(event);
}

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

35 changes: 35 additions & 0 deletions plugins/carlabase/carla.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,22 @@
#ifndef CARLA_H
#define CARLA_H

#define CARLA_MAX_KNOBS 32
#define CARLA_SETTING_PREFIX "PARAM_KNOB_"

// qt
#include <QtCore/QMutex>
#include <QScrollArea>
#include <QGridLayout>
#include <QList>

// carla/source/includes
#include "CarlaNative.h"

// lmms/include/
#include "Instrument.h"
#include "InstrumentView.h"
#include "EffectControls.h"

class QPushButton;

Expand Down Expand Up @@ -67,6 +77,9 @@ class PLUGIN_EXPORT CarlaInstrument : public Instrument

private slots:
void sampleRateChanged();
void refreshParams();
void clearKnobModels();
void knobModelChanged(uint32_t index);

private:
const bool kIsPatchbay;
Expand All @@ -83,8 +96,13 @@ private slots:
QMutex fMutex;

friend class CarlaInstrumentView;

QList<FloatModel*> floatModels;
QDomElement settingsElem;
};

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

class CarlaInstrumentView : public InstrumentView
{
Q_OBJECT
Expand All @@ -96,16 +114,33 @@ class CarlaInstrumentView : public InstrumentView
private slots:
void toggleUI(bool);
void uiClosed();
void refreshButtons();
void onRefreshButton();

private:
virtual void modelChanged();
virtual void timerEvent(QTimerEvent*);

void addKnob(FloatModel& knobModel);
void clearKnobs();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our coding convention says you should use tabs, but most part of this file uses spaces. I don't know which one will be better, but please don't mix them like this. You can change indentation after some discussions.


NativePluginHandle fHandle;
const NativePluginDescriptor* fDescriptor;
int fTimerId;

CarlaInstrument* const p_instrument;

uint32_t lMaxColumns;
uint32_t lCurColumn;
uint32_t lCurRow;

QScrollArea * m_scrollArea;
QGridLayout * m_scrollAreaLayout;

QPushButton * m_toggleUIButton;
QPushButton * m_refreshParamsButton;
};

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

#endif