1 /*
2  * VstEffectControls.cpp - controls for VST effect plugins
3  *
4  * Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
5  *
6  * This file is part of LMMS - https://lmms.io
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public
19  * License along with this program (see COPYING); if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301 USA.
22  *
23  */
24 
25 #include <QDomElement>
26 
27 #include "VstEffectControls.h"
28 #include "VstEffect.h"
29 
30 #include "LocaleHelper.h"
31 #include "MainWindow.h"
32 #include "GuiApplication.h"
33 #include <QMdiArea>
34 #include <QApplication>
35 
36 
37 
VstEffectControls(VstEffect * _eff)38 VstEffectControls::VstEffectControls( VstEffect * _eff ) :
39 	EffectControls( _eff ),
40 	m_effect( _eff ),
41 	m_subWindow( NULL ),
42 	knobFModel( NULL ),
43 	ctrHandle( NULL ),
44 	lastPosInMenu (0),
45 	m_vstGuiVisible ( true )
46 //	m_presetLabel ( NULL )
47 {
48 }
49 
50 
51 
52 
~VstEffectControls()53 VstEffectControls::~VstEffectControls()
54 {
55 	delete ctrHandle;
56 	ctrHandle = NULL;
57 }
58 
59 
60 
61 
loadSettings(const QDomElement & _this)62 void VstEffectControls::loadSettings( const QDomElement & _this )
63 {
64 	//m_effect->closePlugin();
65 	//m_effect->openPlugin( _this.attribute( "plugin" ) );
66 	m_effect->m_pluginMutex.lock();
67 	if( m_effect->m_plugin != NULL )
68 	{
69 		m_vstGuiVisible = _this.attribute( "guivisible" ).toInt();
70 
71 		m_effect->m_plugin->loadSettings( _this );
72 
73 		const QMap<QString, QString> & dump = m_effect->m_plugin->parameterDump();
74 		paramCount = dump.size();
75 		char paramStr[35];
76 		knobFModel = new FloatModel *[ paramCount ];
77 		QStringList s_dumpValues;
78 		for( int i = 0; i < paramCount; i++ )
79 		{
80 			sprintf( paramStr, "param%d", i );
81 			s_dumpValues = dump[ paramStr ].split( ":" );
82 
83 			knobFModel[i] = new FloatModel( 0.0f, 0.0f, 1.0f, 0.01f, this, QString::number(i) );
84 			knobFModel[i]->loadSettings( _this, paramStr );
85 
86 			if( !( knobFModel[ i ]->isAutomated() ||
87 						knobFModel[ i ]->controllerConnection() ) )
88 			{
89 				knobFModel[ i ]->setValue(LocaleHelper::toFloat(s_dumpValues.at(2)));
90 				knobFModel[ i ]->setInitValue(LocaleHelper::toFloat(s_dumpValues.at(2)));
91 			}
92 
93 #if QT_VERSION < 0x050000
94 			connect( knobFModel[i], SIGNAL( dataChanged( Model * ) ),
95 				this, SLOT( setParameter( Model * ) ), Qt::DirectConnection );
96 #else
97 			connect( knobFModel[i], &FloatModel::dataChanged, this,
98 				[this, i]() { setParameter( knobFModel[i] ); }, Qt::DirectConnection);
99 #endif
100 		}
101 
102 	}
103 	m_effect->m_pluginMutex.unlock();
104 }
105 
106 
107 
108 
setParameter(Model * action)109 void VstEffectControls::setParameter( Model * action )
110 {
111 	int knobUNID = action->displayName().toInt();
112 
113 	if ( m_effect->m_plugin != NULL ) {
114 		m_effect->m_plugin->setParam( knobUNID, knobFModel[knobUNID]->value() );
115 	}
116 }
117 
118 
119 
120 
saveSettings(QDomDocument & _doc,QDomElement & _this)121 void VstEffectControls::saveSettings( QDomDocument & _doc, QDomElement & _this )
122 {
123 	_this.setAttribute( "plugin", m_effect->m_key.attributes["file"] );
124 	m_effect->m_pluginMutex.lock();
125 	if( m_effect->m_plugin != NULL )
126 	{
127 		m_effect->m_plugin->saveSettings( _doc, _this );
128 		if (knobFModel != NULL) {
129 			const QMap<QString, QString> & dump = m_effect->m_plugin->parameterDump();
130 			paramCount = dump.size();
131 			char paramStr[35];
132 			for( int i = 0; i < paramCount; i++ )
133 			{
134 				if (knobFModel[i]->isAutomated() || knobFModel[i]->controllerConnection()) {
135 					sprintf( paramStr, "param%d", i);
136 					knobFModel[i]->saveSettings( _doc, _this, paramStr );
137 				}
138 			}
139 		}
140 	}
141 	m_effect->m_pluginMutex.unlock();
142 }
143 
144 
145 
146 
controlCount()147 int VstEffectControls::controlCount()
148 {
149 	return m_effect->m_plugin != NULL ? 1 : 0;
150 }
151 
152 
153 
createView()154 EffectControlDialog *VstEffectControls::createView()
155 {
156 	auto dialog = new VstEffectControlDialog( this );
157 	dialog->togglePluginUI( m_vstGuiVisible );
158 	return dialog;
159 }
160 
161 
162 
163 
managePlugin(void)164 void VstEffectControls::managePlugin( void )
165 {
166 	if ( m_effect->m_plugin != NULL && m_subWindow == NULL ) {
167 		manageVSTEffectView * tt = new manageVSTEffectView( m_effect, this);
168 		ctrHandle = (QObject *)tt;
169 	} else if (m_subWindow != NULL) {
170 		if (m_subWindow->widget()->isVisible() == false ) {
171 			m_scrollArea->show();
172 			m_subWindow->show();
173 		} else {
174 			m_scrollArea->hide();
175 			m_subWindow->hide();
176 		}
177 	}
178 }
179 
180 
181 
182 
183 
savePreset(void)184 void VstEffectControls::savePreset( void )
185 {
186 
187 	if ( m_effect->m_plugin != NULL ) {
188 		m_effect->m_plugin->savePreset( );
189 /*    		bool converted;
190     		QString str = m_vi->m_plugin->currentProgramName().section("/", 0, 0);
191      		if (str != "")
192    			lastPosInMenu = str.toInt(&converted, 10) - 1;
193 		QWidget::update();*/
194 	}
195 
196 }
197 
198 
199 
200 
updateMenu(void)201 void VstEffectControls::updateMenu( void )
202 {
203 
204 	// get all presets -
205 	if ( m_effect->m_plugin != NULL )
206 	{
207 		m_effect->m_plugin->loadProgramNames();
208 		///QWidget::update();
209 
210      		QString str = m_effect->m_plugin->allProgramNames();
211 
212     		QStringList list1 = str.split("|");
213 
214      		QMenu * to_menu = m_selPresetButton->menu();
215     		to_menu->clear();
216 
217     		QAction *presetActions[list1.size()];
218 
219      		for (int i = 0; i < list1.size(); i++) {
220 			presetActions[i] = new QAction(this);
221 			connect(presetActions[i], SIGNAL(triggered()), this, SLOT(selPreset()));
222 
223         		presetActions[i]->setText(QString("%1. %2").arg(QString::number(i+1), list1.at(i)));
224         		presetActions[i]->setData(i);
225 			if (i == lastPosInMenu) {
226         			presetActions[i]->setIcon(embed::getIconPixmap( "sample_file", 16, 16 ));
227 			} else  presetActions[i]->setIcon(embed::getIconPixmap( "edit_copy", 16, 16 ));
228 			to_menu->addAction( presetActions[i] );
229      		}
230 
231 	}
232 
233 }
234 
235 
236 
237 
openPreset(void)238 void VstEffectControls::openPreset( void )
239 {
240 
241 	if ( m_effect->m_plugin != NULL ) {
242 		m_effect->m_plugin->openPreset( );
243     		bool converted;
244     		QString str = m_effect->m_plugin->currentProgramName().section("/", 0, 0);
245      		if (str != "")
246    			lastPosInMenu = str.toInt(&converted, 10) - 1;
247 		//QWidget::update();
248 	}
249 
250 }
251 
252 
253 
254 
rollPreset(void)255 void VstEffectControls::rollPreset( void )
256 {
257 
258 	if ( m_effect->m_plugin != NULL ) {
259 		m_effect->m_plugin->rotateProgram( 1 );
260     		bool converted;
261     		QString str = m_effect->m_plugin->currentProgramName().section("/", 0, 0);
262      		if (str != "")
263    			lastPosInMenu = str.toInt(&converted, 10) - 1;
264 		//QWidget::update();
265 	}
266 }
267 
268 
269 
270 
rolrPreset(void)271 void VstEffectControls::rolrPreset( void )
272 {
273 
274 	if ( m_effect->m_plugin != NULL ) {
275 		m_effect->m_plugin->rotateProgram( -1 );
276     		bool converted;
277     		QString str = m_effect->m_plugin->currentProgramName().section("/", 0, 0);
278      		if (str != "")
279    			lastPosInMenu = str.toInt(&converted, 10) - 1;
280 		//QWidget::update();
281 	}
282 }
283 
284 
285 
286 
selPreset(void)287 void VstEffectControls::selPreset( void )
288 {
289 
290      QAction *action = qobject_cast<QAction *>(sender());
291      if (action)
292          if ( m_effect->m_plugin != NULL ) {
293 		lastPosInMenu = action->data().toInt();
294 		m_effect->m_plugin->setProgram( lastPosInMenu );
295 		//QWidget::update();
296 	 }
297 }
298 
299 
300 
301 
paintEvent(QPaintEvent *)302 void VstEffectControls::paintEvent( QPaintEvent * )
303 {
304 
305 }
306 
307 
308 
309 
manageVSTEffectView(VstEffect * _eff,VstEffectControls * m_vi)310 manageVSTEffectView::manageVSTEffectView( VstEffect * _eff, VstEffectControls * m_vi ) :
311 	m_effect( _eff )
312 {
313 	m_vi2 = m_vi;
314 	widget = new QWidget();
315         m_vi->m_scrollArea = new QScrollArea( widget );
316 	l = new QGridLayout( widget );
317 
318 	m_vi->m_subWindow = gui->mainWindow()->addWindowedWidget(NULL, Qt::SubWindow |
319 			Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
320 	m_vi->m_subWindow->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
321 	m_vi->m_subWindow->setFixedSize( 960, 300);
322 	m_vi->m_subWindow->setWidget(m_vi->m_scrollArea);
323 	m_vi->m_subWindow->setWindowTitle( _eff->m_plugin->name() + tr( " - VST parameter control" ) );
324 	m_vi->m_subWindow->setWindowIcon( PLUGIN_NAME::getIconPixmap( "logo" ) );
325 	m_vi->m_subWindow->setAttribute(Qt::WA_DeleteOnClose, false);
326 
327 
328 	l->setContentsMargins( 20, 10, 10, 10 );
329 	l->setVerticalSpacing( 10 );
330 	l->setHorizontalSpacing( 23 );
331 
332 	m_syncButton = new QPushButton( tr( "VST Sync" ), widget );
333 	connect( m_syncButton, SIGNAL( clicked() ), this,
334 							SLOT( syncPlugin() ) );
335 	m_syncButton->setWhatsThis(
336 		tr( "Click here if you want to synchronize all parameters with VST plugin." ) );
337 
338 	l->addWidget( m_syncButton, 0, 0, 1, 2, Qt::AlignLeft );
339 
340 	m_displayAutomatedOnly = new QPushButton( tr( "Automated" ), widget );
341 	connect( m_displayAutomatedOnly, SIGNAL( clicked() ), this,
342 							SLOT( displayAutomatedOnly() ) );
343 	m_displayAutomatedOnly->setWhatsThis(
344 		tr( "Click here if you want to display automated parameters only." ) );
345 
346 	l->addWidget( m_displayAutomatedOnly, 0, 1, 1, 2, Qt::AlignLeft );
347 
348 
349 	m_closeButton = new QPushButton( tr( "    Close    " ), widget );
350 	connect( m_closeButton, SIGNAL( clicked() ), this,
351 							SLOT( closeWindow() ) );
352 	m_closeButton->setWhatsThis(
353 		tr( "Close VST effect knob-controller window." ) );
354 
355 	l->addWidget( m_closeButton, 0, 2, 1, 7, Qt::AlignLeft );
356 
357 
358 	for( int i = 0; i < 10; i++ )
359 	{
360 		l->addItem( new QSpacerItem( 68, 45, QSizePolicy::Fixed, QSizePolicy::Fixed ), 0, i );
361 	}
362 
363 	const QMap<QString, QString> & dump = m_effect->m_plugin->parameterDump();
364 	m_vi->paramCount = dump.size();
365 
366 	vstKnobs = new Knob *[ m_vi->paramCount ];
367 
368 	bool hasKnobModel = true;
369 	if (m_vi->knobFModel == NULL) {
370 		m_vi->knobFModel = new FloatModel *[ m_vi->paramCount ];
371 		hasKnobModel = false;
372 	}
373 
374 	char paramStr[35];
375 	QStringList s_dumpValues;
376 
377 	for( int i = 0; i < m_vi->paramCount; i++ )
378 	{
379 		sprintf( paramStr, "param%d", i);
380 		s_dumpValues = dump[ paramStr ].split( ":" );
381 
382 		vstKnobs[ i ] = new Knob( knobBright_26, widget, s_dumpValues.at( 1 ) );
383 		vstKnobs[ i ]->setHintText( s_dumpValues.at( 1 ) + ":", "" );
384 		vstKnobs[ i ]->setLabel( s_dumpValues.at( 1 ).left( 15 ) );
385 
386 		if( !hasKnobModel )
387 		{
388 			sprintf( paramStr, "%d", i);
389 			m_vi->knobFModel[ i ] = new FloatModel( LocaleHelper::toFloat(s_dumpValues.at(2)),
390 					0.0f, 1.0f, 0.01f, _eff, tr( paramStr ) );
391 		}
392 
393 		FloatModel * model = m_vi->knobFModel[i];
394 #if QT_VERSION < 0x050000
395 		connect( model, SIGNAL( dataChanged( Model * ) ), this,
396 			SLOT( setParameter( Model * ) ), Qt::DirectConnection );
397 #else
398 		connect( model, &FloatModel::dataChanged, this,
399 			[this, model]() { setParameter( model ); }, Qt::DirectConnection);
400 #endif
401 		vstKnobs[ i ] ->setModel( model );
402 	}
403 
404 	int i = 0;
405 	for( int lrow = 1; lrow < ( int( m_vi->paramCount / 10 ) + 1 ) + 1; lrow++ )
406 	{
407 		for( int lcolumn = 0; lcolumn < 10; lcolumn++ )
408 		{
409 			if( i < m_vi->paramCount )
410 			{
411 				l->addWidget( vstKnobs[i], lrow, lcolumn, Qt::AlignCenter );
412 			}
413 			i++;
414 		}
415 	}
416 
417 	l->setRowStretch( ( int( m_vi->paramCount / 10 ) + 1 ), 1 );
418 	l->setColumnStretch( 10, 1 );
419 
420 	widget->setLayout(l);
421 	widget->setAutoFillBackground(true);
422 
423 	m_vi->m_scrollArea->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn );
424 	m_vi->m_scrollArea->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
425 	m_vi->m_scrollArea->setPalette( QApplication::palette( m_vi->m_scrollArea ) );
426 	m_vi->m_scrollArea->setMinimumHeight( 64 );
427 
428 	m_vi->m_scrollArea->setWidget( widget );
429 
430 	m_vi->m_subWindow->show();
431 }
432 
433 
434 
435 
closeWindow()436 void manageVSTEffectView::closeWindow()
437 {
438 	m_vi2->m_subWindow->hide();
439 }
440 
441 
442 
443 
syncPlugin(void)444 void manageVSTEffectView::syncPlugin( void )
445 {
446 	char paramStr[35];
447 	QStringList s_dumpValues;
448 	const QMap<QString, QString> & dump = m_effect->m_plugin->parameterDump();
449 	float f_value;
450 
451 	for( int i = 0; i < m_vi2->paramCount; i++ )
452 	{
453 		// only not automated knobs are synced from VST
454 		// those auto-setted values are not jurnaled, tracked for undo / redo
455 		if( !( m_vi2->knobFModel[ i ]->isAutomated() ||
456 					m_vi2->knobFModel[ i ]->controllerConnection() ) )
457 		{
458 			sprintf( paramStr, "param%d", i );
459     			s_dumpValues = dump[ paramStr ].split( ":" );
460 			f_value = LocaleHelper::toFloat(s_dumpValues.at(2));
461 			m_vi2->knobFModel[ i ]->setAutomatedValue( f_value );
462 			m_vi2->knobFModel[ i ]->setInitValue( f_value );
463 		}
464 	}
465 }
466 
467 
468 
displayAutomatedOnly(void)469 void manageVSTEffectView::displayAutomatedOnly( void )
470 {
471 	bool isAuto = QString::compare( m_displayAutomatedOnly->text(), tr( "Automated" ) ) == 0;
472 
473 	for( int i = 0; i< m_vi2->paramCount; i++ )
474 	{
475 
476 		if( !( m_vi2->knobFModel[ i ]->isAutomated() ||
477 					m_vi2->knobFModel[ i ]->controllerConnection() ) )
478 		{
479 			if( vstKnobs[ i ]->isVisible() == true  && isAuto )
480 			{
481 				vstKnobs[ i ]->hide();
482 				m_displayAutomatedOnly->setText( "All" );
483 			} else {
484 				vstKnobs[ i ]->show();
485 				m_displayAutomatedOnly->setText( "Automated" );
486 			}
487 		}
488  	}
489 }
490 
491 
492 
493 
setParameter(Model * action)494 void manageVSTEffectView::setParameter( Model * action )
495 {
496 	int knobUNID = action->displayName().toInt();
497 
498 	if ( m_effect->m_plugin != NULL ) {
499 		m_effect->m_plugin->setParam( knobUNID, m_vi2->knobFModel[knobUNID]->value() );
500 	}
501 }
502 
503 
504 
505 
~manageVSTEffectView()506 manageVSTEffectView::~manageVSTEffectView()
507 {
508 	if( m_vi2->knobFModel != NULL )
509 	{
510 		for( int i = 0; i < m_vi2->paramCount; i++ )
511 		{
512 			delete m_vi2->knobFModel[ i ];
513 			delete vstKnobs[ i ];
514 		}
515 	}
516 
517 	if( vstKnobs != NULL )
518 	{
519 		delete [] vstKnobs;
520 		vstKnobs = NULL;
521 	}
522 
523 	if( m_vi2->knobFModel != NULL )
524 	{
525 		delete [] m_vi2->knobFModel;
526 		m_vi2->knobFModel = NULL;
527 	}
528 
529 	if( m_vi2->m_scrollArea != NULL )
530 	{
531 		delete m_vi2->m_scrollArea;
532 		m_vi2->m_scrollArea = NULL;
533 	}
534 
535 	if( m_vi2->m_subWindow != NULL )
536 	{
537 		m_vi2->m_subWindow->setAttribute( Qt::WA_DeleteOnClose );
538 		m_vi2->m_subWindow->close();
539 
540 		if( m_vi2->m_subWindow != NULL )
541 		{
542 			delete m_vi2->m_subWindow;
543 		}
544 		m_vi2->m_subWindow = NULL;
545 	}
546 	//delete m_vi2->m_subWindow;
547 	//m_vi2->m_subWindow = NULL;
548 }
549 
550 
551 
552 
553 
554 
555