1 /*
2  * FxMixerView.cpp - effect-mixer-view for LMMS
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 <QtGlobal>
26 #include <QDebug>
27 
28 #include <QButtonGroup>
29 #include <QInputDialog>
30 #include <QLayout>
31 #include <QMdiArea>
32 #include <QMdiSubWindow>
33 #include <QPainter>
34 #include <QPushButton>
35 #include <QScrollArea>
36 #include <QStyle>
37 #include <QKeyEvent>
38 
39 #include "lmms_math.h"
40 
41 #include "FxMixerView.h"
42 #include "Knob.h"
43 #include "FxLine.h"
44 #include "FxMixer.h"
45 #include "GuiApplication.h"
46 #include "MainWindow.h"
47 #include "Mixer.h"
48 #include "gui_templates.h"
49 #include "InstrumentTrack.h"
50 #include "Song.h"
51 #include "BBTrackContainer.h"
52 
FxMixerView()53 FxMixerView::FxMixerView() :
54 	QWidget(),
55 	ModelView( NULL, this ),
56 	SerializingObjectHook()
57 {
58 	FxMixer * m = Engine::fxMixer();
59 	m->setHook( this );
60 
61 	//QPalette pal = palette();
62 	//pal.setColor( QPalette::Background, QColor( 72, 76, 88 ) );
63 	//setPalette( pal );
64 
65 	setAutoFillBackground( true );
66 	setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
67 
68 	setWindowTitle( tr( "FX-Mixer" ) );
69 	setWindowIcon( embed::getIconPixmap( "fx_mixer" ) );
70 
71 	// main-layout
72 	QHBoxLayout * ml = new QHBoxLayout;
73 
74 	// Set margins
75 	ml->setContentsMargins( 0, 4, 0, 0 );
76 
77 	// Channel area
78 	m_channelAreaWidget = new QWidget;
79 	chLayout = new QHBoxLayout( m_channelAreaWidget );
80 	chLayout->setSizeConstraint( QLayout::SetMinimumSize );
81 	chLayout->setSpacing( 0 );
82 	chLayout->setMargin( 0 );
83 	m_channelAreaWidget->setLayout(chLayout);
84 
85 	// create rack layout before creating the first channel
86 	m_racksWidget = new QWidget;
87 	m_racksLayout = new QStackedLayout( m_racksWidget );
88 	m_racksLayout->setContentsMargins( 0, 0, 0, 0 );
89 	m_racksWidget->setLayout( m_racksLayout );
90 
91 	// add master channel
92 	m_fxChannelViews.resize( m->numChannels() );
93 	m_fxChannelViews[0] = new FxChannelView( this, this, 0 );
94 
95 	m_racksLayout->addWidget( m_fxChannelViews[0]->m_rackView );
96 
97 	FxChannelView * masterView = m_fxChannelViews[0];
98 	ml->addWidget( masterView->m_fxLine, 0, Qt::AlignTop );
99 
100 	QSize fxLineSize = masterView->m_fxLine->size();
101 
102 	// add mixer channels
103 	for( int i = 1; i < m_fxChannelViews.size(); ++i )
104 	{
105 		m_fxChannelViews[i] = new FxChannelView(m_channelAreaWidget, this, i);
106 		chLayout->addWidget( m_fxChannelViews[i]->m_fxLine );
107 	}
108 
109 	// add the scrolling section to the main layout
110 	// class solely for scroll area to pass key presses down
111 	class ChannelArea : public QScrollArea
112 	{
113 		public:
114 			ChannelArea( QWidget * parent, FxMixerView * mv ) :
115 				QScrollArea( parent ), m_mv( mv ) {}
116 			~ChannelArea() {}
117 			virtual void keyPressEvent( QKeyEvent * e )
118 			{
119 				m_mv->keyPressEvent( e );
120 			}
121 		private:
122 			FxMixerView * m_mv;
123 	};
124 	channelArea = new ChannelArea( this, this );
125 	channelArea->setWidget( m_channelAreaWidget );
126 	channelArea->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
127 	channelArea->setFrameStyle( QFrame::NoFrame );
128 	channelArea->setMinimumWidth( fxLineSize.width() * 6 );
129 	channelArea->setFixedHeight( fxLineSize.height() +
130 			style()->pixelMetric( QStyle::PM_ScrollBarExtent ) );
131 	ml->addWidget( channelArea, 1, Qt::AlignTop );
132 
133 	// show the add new effect channel button
134 	QPushButton * newChannelBtn = new QPushButton( embed::getIconPixmap( "new_channel" ), QString::null, this );
135 	newChannelBtn->setObjectName( "newChannelBtn" );
136 	newChannelBtn->setFixedSize( fxLineSize );
137 	connect( newChannelBtn, SIGNAL( clicked() ), this, SLOT( addNewChannel() ) );
138 	ml->addWidget( newChannelBtn, 0, Qt::AlignTop );
139 
140 
141 	// add the stacked layout for the effect racks of fx channels
142 	ml->addWidget( m_racksWidget, 0, Qt::AlignTop | Qt::AlignRight );
143 
144 	setCurrentFxLine( m_fxChannelViews[0]->m_fxLine );
145 
146 	setLayout( ml );
147 	updateGeometry();
148 
149 	// timer for updating faders
150 	connect( gui->mainWindow(), SIGNAL( periodicUpdate() ),
151 					this, SLOT( updateFaders() ) );
152 
153 
154 	// add ourself to workspace
155 	QMdiSubWindow * subWin = gui->mainWindow()->addWindowedWidget( this );
156 	Qt::WindowFlags flags = subWin->windowFlags();
157 	flags &= ~Qt::WindowMaximizeButtonHint;
158 	subWin->setWindowFlags( flags );
159 	layout()->setSizeConstraint( QLayout::SetMinimumSize );
160 	subWin->layout()->setSizeConstraint( QLayout::SetMinAndMaxSize );
161 
162 	parentWidget()->setAttribute( Qt::WA_DeleteOnClose, false );
163 	parentWidget()->move( 5, 310 );
164 
165 	// we want to receive dataChanged-signals in order to update
166 	setModel( m );
167 }
168 
~FxMixerView()169 FxMixerView::~FxMixerView()
170 {
171 	for (int i=0; i<m_fxChannelViews.size(); i++)
172 	{
173 		delete m_fxChannelViews.at(i);
174 	}
175 }
176 
177 
178 
addNewChannel()179 int FxMixerView::addNewChannel()
180 {
181 	// add new fx mixer channel and redraw the form.
182 	FxMixer * mix = Engine::fxMixer();
183 
184 	int newChannelIndex = mix->createChannel();
185 	m_fxChannelViews.push_back(new FxChannelView(m_channelAreaWidget, this,
186 												 newChannelIndex));
187 	chLayout->addWidget( m_fxChannelViews[newChannelIndex]->m_fxLine );
188 	m_racksLayout->addWidget( m_fxChannelViews[newChannelIndex]->m_rackView );
189 
190 	updateFxLine(newChannelIndex);
191 
192 	updateMaxChannelSelector();
193 
194 	return newChannelIndex;
195 }
196 
197 
refreshDisplay()198 void FxMixerView::refreshDisplay()
199 {
200 	// delete all views and re-add them
201 	for( int i = 1; i<m_fxChannelViews.size(); ++i )
202 	{
203 		chLayout->removeWidget(m_fxChannelViews[i]->m_fxLine);
204 		m_racksLayout->removeWidget( m_fxChannelViews[i]->m_rackView );
205 		delete m_fxChannelViews[i]->m_fader;
206 		delete m_fxChannelViews[i]->m_muteBtn;
207 		delete m_fxChannelViews[i]->m_soloBtn;
208 		delete m_fxChannelViews[i]->m_fxLine;
209 		delete m_fxChannelViews[i]->m_rackView;
210 		delete m_fxChannelViews[i];
211 	}
212 	m_channelAreaWidget->adjustSize();
213 
214 	// re-add the views
215 	m_fxChannelViews.resize(Engine::fxMixer()->numChannels());
216 	for( int i = 1; i < m_fxChannelViews.size(); ++i )
217 	{
218 		m_fxChannelViews[i] = new FxChannelView(m_channelAreaWidget, this, i);
219 		chLayout->addWidget(m_fxChannelViews[i]->m_fxLine);
220 		m_racksLayout->addWidget( m_fxChannelViews[i]->m_rackView );
221 	}
222 
223 	// set selected fx line to 0
224 	setCurrentFxLine( 0 );
225 
226 	// update all fx lines
227 	for( int i = 0; i < m_fxChannelViews.size(); ++i )
228 	{
229 		updateFxLine( i );
230 	}
231 
232 	updateMaxChannelSelector();
233 }
234 
235 
236 // update the and max. channel number for every instrument
updateMaxChannelSelector()237 void FxMixerView::updateMaxChannelSelector()
238 {
239 	QVector<Track *> songTrackList = Engine::getSong()->tracks();
240 	QVector<Track *> bbTrackList = Engine::getBBTrackContainer()->tracks();
241 
242 	QVector<Track *> trackLists[] = {songTrackList, bbTrackList};
243 	for(int tl=0; tl<2; ++tl)
244 	{
245 		QVector<Track *> trackList = trackLists[tl];
246 		for(int i=0; i<trackList.size(); ++i)
247 		{
248 			if( trackList[i]->type() == Track::InstrumentTrack )
249 			{
250 				InstrumentTrack * inst = (InstrumentTrack *) trackList[i];
251 				inst->effectChannelModel()->setRange(0,
252 					m_fxChannelViews.size()-1,1);
253 			}
254 		}
255 	}
256 }
257 
258 
saveSettings(QDomDocument & _doc,QDomElement & _this)259 void FxMixerView::saveSettings( QDomDocument & _doc, QDomElement & _this )
260 {
261 	MainWindow::saveWidgetState( this, _this );
262 }
263 
264 
265 
266 
loadSettings(const QDomElement & _this)267 void FxMixerView::loadSettings( const QDomElement & _this )
268 {
269 	MainWindow::restoreWidgetState( this, _this );
270 }
271 
272 
FxChannelView(QWidget * _parent,FxMixerView * _mv,int channelIndex)273 FxMixerView::FxChannelView::FxChannelView(QWidget * _parent, FxMixerView * _mv,
274 										  int channelIndex )
275 {
276 	m_fxLine = new FxLine(_parent, _mv, channelIndex);
277 
278 	FxChannel *fxChannel = Engine::fxMixer()->effectChannel(channelIndex);
279 
280 	m_fader = new Fader( &fxChannel->m_volumeModel,
281 					tr( "FX Fader %1" ).arg( channelIndex ), m_fxLine );
282 	m_fader->setLevelsDisplayedInDBFS();
283 	m_fader->setMinPeak(dbfsToAmp(-42));
284 	m_fader->setMaxPeak(dbfsToAmp(9));
285 
286 	m_fader->move( 16-m_fader->width()/2,
287 					m_fxLine->height()-
288 					m_fader->height()-5 );
289 
290 	m_muteBtn = new PixmapButton( m_fxLine, tr( "Mute" ) );
291 	m_muteBtn->setModel( &fxChannel->m_muteModel );
292 	m_muteBtn->setActiveGraphic(
293 				embed::getIconPixmap( "led_off" ) );
294 	m_muteBtn->setInactiveGraphic(
295 				embed::getIconPixmap( "led_green" ) );
296 	m_muteBtn->setCheckable( true );
297 	m_muteBtn->move( 9,  m_fader->y()-11);
298 	ToolTip::add( m_muteBtn, tr( "Mute this FX channel" ) );
299 
300 	m_soloBtn = new PixmapButton( m_fxLine, tr( "Solo" ) );
301 	m_soloBtn->setModel( &fxChannel->m_soloModel );
302 	m_soloBtn->setActiveGraphic(
303 				embed::getIconPixmap( "led_red" ) );
304 	m_soloBtn->setInactiveGraphic(
305 				embed::getIconPixmap( "led_off" ) );
306 	m_soloBtn->setCheckable( true );
307 	m_soloBtn->move( 9,  m_fader->y()-21);
308 	connect(&fxChannel->m_soloModel, SIGNAL( dataChanged() ),
309 			_mv, SLOT ( toggledSolo() ), Qt::DirectConnection );
310 	ToolTip::add( m_soloBtn, tr( "Solo FX channel" ) );
311 
312 	// Create EffectRack for the channel
313 	m_rackView = new EffectRackView( &fxChannel->m_fxChain, _mv->m_racksWidget );
314 	m_rackView->setFixedSize( 245, FxLine::FxLineHeight );
315 }
316 
317 
setChannelIndex(int index)318 void FxMixerView::FxChannelView::setChannelIndex( int index )
319 {
320 	FxChannel* fxChannel = Engine::fxMixer()->effectChannel( index );
321 
322 	m_fader->setModel( &fxChannel->m_volumeModel );
323 	m_muteBtn->setModel( &fxChannel->m_muteModel );
324 	m_soloBtn->setModel( &fxChannel->m_soloModel );
325 	m_rackView->setModel( &fxChannel->m_fxChain );
326 }
327 
328 
toggledSolo()329 void FxMixerView::toggledSolo()
330 {
331 	Engine::fxMixer()->toggledSolo();
332 }
333 
334 
335 
setCurrentFxLine(FxLine * _line)336 void FxMixerView::setCurrentFxLine( FxLine * _line )
337 {
338 	// select
339 	m_currentFxLine = _line;
340 	m_racksLayout->setCurrentWidget( m_fxChannelViews[ _line->channelIndex() ]->m_rackView );
341 
342 	// set up send knob
343 	for(int i = 0; i < m_fxChannelViews.size(); ++i)
344 	{
345 		updateFxLine(i);
346 	}
347 }
348 
349 
updateFxLine(int index)350 void FxMixerView::updateFxLine(int index)
351 {
352 	FxMixer * mix = Engine::fxMixer();
353 
354 	// does current channel send to this channel?
355 	int selIndex = m_currentFxLine->channelIndex();
356 	FxLine * thisLine = m_fxChannelViews[index]->m_fxLine;
357 	thisLine->setToolTip( Engine::fxMixer()->effectChannel( index )->m_name );
358 
359 	FloatModel * sendModel = mix->channelSendModel(selIndex, index);
360 	if( sendModel == NULL )
361 	{
362 		// does not send, hide send knob
363 		thisLine->m_sendKnob->setVisible(false);
364 	}
365 	else
366 	{
367 		// it does send, show knob and connect
368 		thisLine->m_sendKnob->setVisible(true);
369 		thisLine->m_sendKnob->setModel(sendModel);
370 	}
371 
372 	// disable the send button if it would cause an infinite loop
373 	thisLine->m_sendBtn->setVisible(! mix->isInfiniteLoop(selIndex, index));
374 	thisLine->m_sendBtn->updateLightStatus();
375 	thisLine->update();
376 }
377 
378 
deleteChannel(int index)379 void FxMixerView::deleteChannel(int index)
380 {
381 	// can't delete master
382 	if( index == 0 ) return;
383 
384 	// remember selected line
385 	int selLine = m_currentFxLine->channelIndex();
386 
387 	// in case the deleted channel is soloed or the remaining
388 	// channels will be left in a muted state
389 	Engine::fxMixer()->clearChannel(index);
390 
391 	// delete the real channel
392 	Engine::fxMixer()->deleteChannel(index);
393 
394 	// delete the view
395 	chLayout->removeWidget(m_fxChannelViews[index]->m_fxLine);
396 	m_racksLayout->removeWidget( m_fxChannelViews[index]->m_rackView );
397 	delete m_fxChannelViews[index]->m_fader;
398 	delete m_fxChannelViews[index]->m_muteBtn;
399 	delete m_fxChannelViews[index]->m_soloBtn;
400 	// delete fxLine later to prevent a crash when deleting from context menu
401 	m_fxChannelViews[index]->m_fxLine->hide();
402 	m_fxChannelViews[index]->m_fxLine->deleteLater();
403 	delete m_fxChannelViews[index]->m_rackView;
404 	delete m_fxChannelViews[index];
405 	m_channelAreaWidget->adjustSize();
406 
407 	// make sure every channel knows what index it is
408 	for(int i=0; i<m_fxChannelViews.size(); ++i)
409 	{
410 		if( i > index )
411 		{
412 			m_fxChannelViews[i]->m_fxLine->setChannelIndex(i-1);
413 		}
414 	}
415 	m_fxChannelViews.remove(index);
416 
417 	// select the next channel
418 	if( selLine >= m_fxChannelViews.size() )
419 	{
420 		selLine = m_fxChannelViews.size()-1;
421 	}
422 	setCurrentFxLine(selLine);
423 
424 	updateMaxChannelSelector();
425 }
426 
427 
428 
deleteUnusedChannels()429 void FxMixerView::deleteUnusedChannels()
430 {
431 	TrackContainer::TrackList tracks;
432 	tracks += Engine::getSong()->tracks();
433 	tracks += Engine::getBBTrackContainer()->tracks();
434 
435 	// go through all FX Channels
436 	for(int i = m_fxChannelViews.size()-1; i > 0; --i)
437 	{
438 		// check if an instrument references to the current channel
439 		bool empty=true;
440 		for( Track* t : tracks )
441 		{
442 			if( t->type() == Track::InstrumentTrack )
443 			{
444 				InstrumentTrack* inst = dynamic_cast<InstrumentTrack *>( t );
445 				if( i == inst->effectChannelModel()->value(0) )
446 				{
447 					empty=false;
448 					break;
449 				}
450 			}
451 		}
452 		FxChannel * ch = Engine::fxMixer()->effectChannel( i );
453 		// delete channel if no references found
454 		if( empty && ch->m_receives.isEmpty() )
455 		{
456 			deleteChannel( i );
457 		}
458 	}
459 }
460 
461 
462 
moveChannelLeft(int index,int focusIndex)463 void FxMixerView::moveChannelLeft(int index, int focusIndex)
464 {
465 	// can't move master or first channel left or last channel right
466 	if( index <= 1 || index >= m_fxChannelViews.size() ) return;
467 
468 	FxMixer *m = Engine::fxMixer();
469 
470 	// Move instruments channels
471 	m->moveChannelLeft( index );
472 
473 	// Update widgets models
474 	m_fxChannelViews[index]->setChannelIndex( index );
475 	m_fxChannelViews[index - 1]->setChannelIndex( index - 1 );
476 
477 	// Focus on new position
478 	setCurrentFxLine( focusIndex );
479 }
480 
481 
482 
moveChannelLeft(int index)483 void FxMixerView::moveChannelLeft(int index)
484 {
485 	moveChannelLeft( index, index - 1 );
486 }
487 
488 
489 
moveChannelRight(int index)490 void FxMixerView::moveChannelRight(int index)
491 {
492 	moveChannelLeft( index + 1, index + 1 );
493 }
494 
495 
496 
keyPressEvent(QKeyEvent * e)497 void FxMixerView::keyPressEvent(QKeyEvent * e)
498 {
499 	switch(e->key())
500 	{
501 		case Qt::Key_Delete:
502 			deleteChannel(m_currentFxLine->channelIndex());
503 			break;
504 		case Qt::Key_Left:
505 			if( e->modifiers() & Qt::AltModifier )
506 			{
507 				moveChannelLeft( m_currentFxLine->channelIndex() );
508 			}
509 			else
510 			{
511 				// select channel to the left
512 				setCurrentFxLine( m_currentFxLine->channelIndex()-1 );
513 			}
514 			break;
515 		case Qt::Key_Right:
516 			if( e->modifiers() & Qt::AltModifier )
517 			{
518 				moveChannelRight( m_currentFxLine->channelIndex() );
519 			}
520 			else
521 			{
522 				// select channel to the right
523 				setCurrentFxLine( m_currentFxLine->channelIndex()+1 );
524 			}
525 			break;
526 		case Qt::Key_Insert:
527 			if ( e->modifiers() & Qt::ShiftModifier )
528 			{
529 				addNewChannel();
530 			}
531 			break;
532 	}
533 }
534 
535 
536 
closeEvent(QCloseEvent * _ce)537 void FxMixerView::closeEvent( QCloseEvent * _ce )
538  {
539 	if( parentWidget() )
540 	{
541 		parentWidget()->hide();
542 	}
543 	else
544 	{
545 		hide();
546 	}
547 	_ce->ignore();
548  }
549 
550 
551 
setCurrentFxLine(int _line)552 void FxMixerView::setCurrentFxLine( int _line )
553 {
554 	if( _line >= 0 && _line < m_fxChannelViews.size() )
555 	{
556 		setCurrentFxLine( m_fxChannelViews[_line]->m_fxLine );
557 	}
558 }
559 
560 
561 
clear()562 void FxMixerView::clear()
563 {
564 	Engine::fxMixer()->clear();
565 
566 	refreshDisplay();
567 }
568 
569 
570 
571 
updateFaders()572 void FxMixerView::updateFaders()
573 {
574 	FxMixer * m = Engine::fxMixer();
575 
576 	// apply master gain
577 	m->effectChannel(0)->m_peakLeft *= Engine::mixer()->masterGain();
578 	m->effectChannel(0)->m_peakRight *= Engine::mixer()->masterGain();
579 
580 	for( int i = 0; i < m_fxChannelViews.size(); ++i )
581 	{
582 		const float opl = m_fxChannelViews[i]->m_fader->getPeak_L();
583 		const float opr = m_fxChannelViews[i]->m_fader->getPeak_R();
584 		const float fallOff = 1.07;
585 		if( m->effectChannel(i)->m_peakLeft > opl )
586 		{
587 			m_fxChannelViews[i]->m_fader->setPeak_L( m->effectChannel(i)->m_peakLeft );
588 			m->effectChannel(i)->m_peakLeft = 0;
589 		}
590 		else
591 		{
592 			m_fxChannelViews[i]->m_fader->setPeak_L( opl/fallOff );
593 		}
594 
595 		if( m->effectChannel(i)->m_peakRight > opr )
596 		{
597 			m_fxChannelViews[i]->m_fader->setPeak_R( m->effectChannel(i)->m_peakRight );
598 			m->effectChannel(i)->m_peakRight = 0;
599 		}
600 		else
601 		{
602 			m_fxChannelViews[i]->m_fader->setPeak_R( opr/fallOff );
603 		}
604 	}
605 }
606