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