/[svn]/qsampler/trunk/src/qsamplerChannelStrip.cpp
ViewVC logotype

Contents of /qsampler/trunk/src/qsamplerChannelStrip.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2979 - (show annotations) (download)
Tue Aug 16 15:34:45 2016 UTC (7 years, 7 months ago) by capela
File size: 17741 byte(s)
- Improved channel strip auto-arranger.
1 // qsamplerChannelStrip.cpp
2 //
3 /****************************************************************************
4 Copyright (C) 2004-2016, rncbc aka Rui Nuno Capela. All rights reserved.
5 Copyright (C) 2007, 2008, 2014 Christian Schoenebeck
6
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21 *****************************************************************************/
22
23 #include "qsamplerAbout.h"
24 #include "qsamplerChannelStrip.h"
25
26 #include "qsamplerMainForm.h"
27
28 #include "qsamplerChannelFxForm.h"
29
30 #include <QMessageBox>
31 #include <QDragEnterEvent>
32 #include <QFileInfo>
33 #include <QTimer>
34 #include <QUrl>
35 #include <QMenu>
36
37 #if QT_VERSION >= 0x050000
38 #include <QMimeData>
39 #endif
40
41 // Channel status/usage usage limit control.
42 #define QSAMPLER_ERROR_LIMIT 3
43
44 // Needed for lroundf()
45 #include <math.h>
46
47 #ifndef CONFIG_ROUND
48 static inline long lroundf ( float x )
49 {
50 if (x >= 0.0f)
51 return long(x + 0.5f);
52 else
53 return long(x - 0.5f);
54 }
55 #endif
56
57
58 namespace QSampler {
59
60 //-------------------------------------------------------------------------
61 // QSampler::ChannelStrip -- Channel strip form implementation.
62 //
63
64 // MIDI activity pixmap common resources.
65 int ChannelStrip::g_iMidiActivityRefCount = 0;
66 QPixmap *ChannelStrip::g_pMidiActivityLedOn = NULL;
67 QPixmap *ChannelStrip::g_pMidiActivityLedOff = NULL;
68
69 // Channel strip activation/selection.
70 ChannelStrip *ChannelStrip::g_pSelectedStrip = NULL;
71
72 ChannelStrip::ChannelStrip ( QWidget* pParent, Qt::WindowFlags wflags )
73 : QWidget(pParent, wflags)
74 {
75 m_ui.setupUi(this);
76
77 // Initialize locals.
78 m_pChannel = NULL;
79 m_iDirtyChange = 0;
80 m_iErrorCount = 0;
81 m_instrumentListPopupMenu = NULL;
82
83 if (++g_iMidiActivityRefCount == 1) {
84 g_pMidiActivityLedOn = new QPixmap(":/images/ledon1.png");
85 g_pMidiActivityLedOff = new QPixmap(":/images/ledoff1.png");
86 }
87
88 m_ui.MidiActivityLabel->setPixmap(*g_pMidiActivityLedOff);
89
90 #ifndef CONFIG_EVENT_CHANNEL_MIDI
91 m_ui.MidiActivityLabel->setToolTip("MIDI activity (disabled)");
92 #endif
93
94 m_pMidiActivityTimer = new QTimer(this);
95 m_pMidiActivityTimer->setSingleShot(true);
96
97 QObject::connect(m_pMidiActivityTimer,
98 SIGNAL(timeout()),
99 SLOT(midiActivityLedOff())
100 );
101
102 // Try to restore normal window positioning.
103 adjustSize();
104
105 QObject::connect(m_ui.ChannelSetupPushButton,
106 SIGNAL(clicked()),
107 SLOT(channelSetup()));
108 QObject::connect(m_ui.ChannelMutePushButton,
109 SIGNAL(toggled(bool)),
110 SLOT(channelMute(bool)));
111 QObject::connect(m_ui.ChannelSoloPushButton,
112 SIGNAL(toggled(bool)),
113 SLOT(channelSolo(bool)));
114 QObject::connect(m_ui.ChannelVolumeSlider,
115 SIGNAL(valueChanged(int)),
116 SLOT(volumeChanged(int)));
117 QObject::connect(m_ui.ChannelVolumeSpinBox,
118 SIGNAL(valueChanged(int)),
119 SLOT(volumeChanged(int)));
120 QObject::connect(m_ui.ChannelEditPushButton,
121 SIGNAL(clicked()),
122 SLOT(channelEdit()));
123 QObject::connect(m_ui.ChannelFxPushButton,
124 SIGNAL(clicked()),
125 SLOT(channelFxEdit()));
126
127 setSelected(false);
128 }
129
130
131 ChannelStrip::~ChannelStrip (void)
132 {
133 setSelected(false);
134
135 // Destroy existing channel descriptor.
136 if (m_pChannel)
137 delete m_pChannel;
138 m_pChannel = NULL;
139
140 if (--g_iMidiActivityRefCount == 0) {
141 if (g_pMidiActivityLedOn)
142 delete g_pMidiActivityLedOn;
143 g_pMidiActivityLedOn = NULL;
144 if (g_pMidiActivityLedOff)
145 delete g_pMidiActivityLedOff;
146 g_pMidiActivityLedOff = NULL;
147 }
148 }
149
150
151 // Window drag-n-drop event handlers.
152 void ChannelStrip::dragEnterEvent ( QDragEnterEvent* pDragEnterEvent )
153 {
154 if (m_pChannel == NULL)
155 return;
156
157 bool bAccept = false;
158
159 if (pDragEnterEvent->source() == NULL) {
160 const QMimeData *pMimeData = pDragEnterEvent->mimeData();
161 if (pMimeData && pMimeData->hasUrls()) {
162 QListIterator<QUrl> iter(pMimeData->urls());
163 while (iter.hasNext()) {
164 const QString& sFilename = iter.next().toLocalFile();
165 if (!sFilename.isEmpty()) {
166 // bAccept = Channel::isDlsInstrumentFile(sFilename);
167 bAccept = QFileInfo(sFilename).exists();
168 break;
169 }
170 }
171 }
172 }
173
174 if (bAccept)
175 pDragEnterEvent->accept();
176 else
177 pDragEnterEvent->ignore();
178 }
179
180
181 void ChannelStrip::dropEvent ( QDropEvent* pDropEvent )
182 {
183 if (m_pChannel == NULL)
184 return;
185
186 if (pDropEvent->source())
187 return;
188
189 const QMimeData *pMimeData = pDropEvent->mimeData();
190 if (pMimeData && pMimeData->hasUrls()) {
191 QStringList files;
192 QListIterator<QUrl> iter(pMimeData->urls());
193 while (iter.hasNext()) {
194 const QString& sFilename = iter.next().toLocalFile();
195 if (!sFilename.isEmpty()) {
196 // Go and set the dropped instrument filename...
197 m_pChannel->setInstrument(sFilename, 0);
198 // Open up the channel dialog.
199 channelSetup();
200 break;
201 }
202 }
203 }
204 }
205
206
207 // Channel strip setup formal initializer.
208 void ChannelStrip::setup ( Channel *pChannel )
209 {
210 // Destroy any previous channel descriptor;
211 // (remember that once setup we own it!)
212 if (m_pChannel)
213 delete m_pChannel;
214
215 // Set the new one...
216 m_pChannel = pChannel;
217
218 // Stabilize this around.
219 updateChannelInfo();
220
221 // We'll accept drops from now on...
222 if (m_pChannel)
223 setAcceptDrops(true);
224 }
225
226
227 // Channel secriptor accessor.
228 Channel *ChannelStrip::channel (void) const
229 {
230 return m_pChannel;
231 }
232
233
234 // Messages view font accessors.
235 QFont ChannelStrip::displayFont (void) const
236 {
237 return m_ui.EngineNameTextLabel->font();
238 }
239
240
241 void ChannelStrip::setDisplayFont ( const QFont & font )
242 {
243 m_ui.EngineNameTextLabel->setFont(font);
244 m_ui.MidiPortChannelTextLabel->setFont(font);
245 m_ui.InstrumentNamePushButton->setFont(font);
246 m_ui.InstrumentStatusTextLabel->setFont(font);
247 }
248
249
250 // Channel display background effect.
251 void ChannelStrip::setDisplayEffect ( bool bDisplayEffect )
252 {
253 QPalette pal;
254 pal.setColor(QPalette::Foreground, Qt::yellow);
255 pal.setColor(QPalette::ButtonText, Qt::yellow);
256 m_ui.EngineNameTextLabel->setPalette(pal);
257 m_ui.MidiPortChannelTextLabel->setPalette(pal);
258 pal.setColor(QPalette::Foreground, Qt::green);
259 pal.setColor(QPalette::ButtonText, Qt::green);
260 if (bDisplayEffect) {
261 QPixmap pm(":/images/displaybg1.png");
262 pal.setBrush(QPalette::Background, QBrush(pm));
263 } else {
264 pal.setColor(QPalette::Background, Qt::black);
265 }
266 m_ui.ChannelInfoFrame->setPalette(pal);
267 m_ui.InstrumentNamePushButton->setPalette(pal);
268 m_ui.StreamVoiceCountTextLabel->setPalette(pal);
269 }
270
271
272 // Maximum volume slider accessors.
273 void ChannelStrip::setMaxVolume ( int iMaxVolume )
274 {
275 m_iDirtyChange++;
276 m_ui.ChannelVolumeSlider->setRange(0, iMaxVolume);
277 m_ui.ChannelVolumeSpinBox->setRange(0, iMaxVolume);
278 m_iDirtyChange--;
279 }
280
281
282 // Channel setup dialog slot.
283 bool ChannelStrip::channelSetup (void)
284 {
285 if (m_pChannel == NULL)
286 return false;
287
288 // Invoke the channel setup dialog.
289 const bool bResult = m_pChannel->channelSetup(this);
290 // Notify that this channel has changed.
291 if (bResult)
292 emit channelChanged(this);
293
294 return bResult;
295 }
296
297
298 // Channel mute slot.
299 bool ChannelStrip::channelMute ( bool bMute )
300 {
301 if (m_pChannel == NULL)
302 return false;
303
304 // Invoke the channel mute method.
305 const bool bResult = m_pChannel->setChannelMute(bMute);
306 // Notify that this channel has changed.
307 if (bResult)
308 emit channelChanged(this);
309
310 return bResult;
311 }
312
313
314 // Channel solo slot.
315 bool ChannelStrip::channelSolo ( bool bSolo )
316 {
317 if (m_pChannel == NULL)
318 return false;
319
320 // Invoke the channel solo method.
321 const bool bResult = m_pChannel->setChannelSolo(bSolo);
322 // Notify that this channel has changed.
323 if (bResult)
324 emit channelChanged(this);
325
326 return bResult;
327 }
328
329
330 // Channel edit slot.
331 void ChannelStrip::channelEdit (void)
332 {
333 if (m_pChannel == NULL)
334 return;
335
336 m_pChannel->editChannel();
337 }
338
339 bool ChannelStrip::channelFxEdit (void)
340 {
341 MainForm *pMainForm = MainForm::getInstance();
342 if (!pMainForm || !channel())
343 return false;
344
345 pMainForm->appendMessages(QObject::tr("channel fx sends..."));
346
347 bool bResult = false;
348
349 #if CONFIG_FXSEND
350 ChannelFxForm *pChannelFxForm =
351 new ChannelFxForm(channel(), parentWidget());
352 if (pChannelFxForm) {
353 //pChannelForm->setup(this);
354 bResult = pChannelFxForm->exec();
355 delete pChannelFxForm;
356 }
357 #else // CONFIG_FXSEND
358 QMessageBox::critical(this,
359 QSAMPLER_TITLE ": " + tr("Unavailable"),
360 tr("Sorry, QSampler was built without FX send support!\n\n"
361 "(Make sure you have a recent liblscp when recompiling QSampler)"));
362 #endif // CONFIG_FXSEND
363
364 return bResult;
365 }
366
367
368 // Channel reset slot.
369 bool ChannelStrip::channelReset (void)
370 {
371 if (m_pChannel == NULL)
372 return false;
373
374 // Invoke the channel reset method.
375 const bool bResult = m_pChannel->channelReset();
376 // Notify that this channel has changed.
377 if (bResult)
378 emit channelChanged(this);
379
380 return bResult;
381 }
382
383
384 // Update the channel instrument name.
385 bool ChannelStrip::updateInstrumentName ( bool bForce )
386 {
387 if (m_pChannel == NULL)
388 return false;
389
390 // Do we refresh the actual name?
391 if (bForce)
392 m_pChannel->updateInstrumentName();
393
394 // Instrument name...
395 if (m_pChannel->instrumentName().isEmpty()) {
396 if (m_pChannel->instrumentStatus() >= 0) {
397 m_ui.InstrumentNamePushButton->setText(
398 ' ' + Channel::loadingInstrument());
399 } else {
400 m_ui.InstrumentNamePushButton->setText(
401 ' ' + Channel::noInstrumentName());
402 }
403 } else {
404 m_ui.InstrumentNamePushButton->setText(
405 ' ' + m_pChannel->instrumentName());
406 }
407
408 bool bShowInstrumentPopup = false;
409
410 // Instrument list popup (for fast switching among sounds of the same file)
411 if (!m_pChannel->instrumentFile().isEmpty()) {
412 const QStringList instruments
413 = Channel::getInstrumentList(m_pChannel->instrumentFile(), true);
414 if (!instruments.isEmpty()) {
415 bShowInstrumentPopup = true;
416 if (!m_instrumentListPopupMenu) {
417 m_instrumentListPopupMenu
418 = new QMenu(m_ui.InstrumentNamePushButton);
419 m_instrumentListPopupMenu->setTitle(tr("Instruments"));
420 // for cosmetical reasons, should have at least
421 // the width of the instrument name label...
422 m_instrumentListPopupMenu->setMinimumWidth(120);
423 m_ui.InstrumentNamePushButton->setMenu(m_instrumentListPopupMenu);
424 QObject::connect(m_instrumentListPopupMenu,
425 SIGNAL(triggered(QAction*)),
426 SLOT(instrumentListPopupItemClicked(QAction *)));
427 } else {
428 m_instrumentListPopupMenu->clear();
429 }
430 QAction *action;
431 for (int i = 0; i < instruments.size(); ++i) {
432 action = m_instrumentListPopupMenu->addAction(instruments.at(i));
433 action->setData(i);
434 action->setCheckable(true);
435 action->setChecked(i == m_pChannel->instrumentNr());
436 }
437 }
438 }
439
440 if (!bShowInstrumentPopup && m_instrumentListPopupMenu) {
441 delete m_instrumentListPopupMenu;
442 m_instrumentListPopupMenu = NULL;
443 }
444
445 return true;
446 }
447
448
449 void ChannelStrip::instrumentListPopupItemClicked ( QAction *action )
450 {
451 if (!action) return;
452
453 QVariant data = action->data();
454 if (data.isValid() && !m_pChannel->instrumentFile().isEmpty()) {
455 m_pChannel->loadInstrument(m_pChannel->instrumentFile(), data.toInt());
456 emit channelChanged(this);
457 }
458 }
459
460
461 // Do the dirty volume change.
462 bool ChannelStrip::updateChannelVolume (void)
463 {
464 if (m_pChannel == NULL)
465 return false;
466
467 // Convert...
468 int iVolume = ::lroundf(100.0f * m_pChannel->volume());
469 // And clip...
470 if (iVolume < 0)
471 iVolume = 0;
472
473 // Flag it here, to avoid infinite recursion.
474 m_iDirtyChange++;
475 m_ui.ChannelVolumeSlider->setValue(iVolume);
476 m_ui.ChannelVolumeSpinBox->setValue(iVolume);
477 m_iDirtyChange--;
478
479 return true;
480 }
481
482
483 // Update whole channel info state.
484 bool ChannelStrip::updateChannelInfo (void)
485 {
486 if (m_pChannel == NULL)
487 return false;
488
489 // Check for error limit/recycle...
490 if (m_iErrorCount > QSAMPLER_ERROR_LIMIT)
491 return true;
492
493 // Update strip caption.
494 const QString& sText = m_pChannel->channelName();
495 setWindowTitle(sText);
496 m_ui.ChannelSetupPushButton->setText('&' + sText);
497
498 // Check if we're up and connected.
499 MainForm *pMainForm = MainForm::getInstance();
500 if (pMainForm->client() == NULL)
501 return false;
502
503 // Read actual channel information.
504 m_pChannel->updateChannelInfo();
505
506 // Engine name...
507 if (m_pChannel->engineName().isEmpty()) {
508 m_ui.EngineNameTextLabel->setText(
509 ' ' + Channel::noEngineName());
510 } else {
511 m_ui.EngineNameTextLabel->setText(
512 ' ' + m_pChannel->engineName());
513 }
514
515 // Instrument name...
516 updateInstrumentName(false);
517
518 // MIDI Port/Channel...
519 QString sMidiPortChannel = QString::number(m_pChannel->midiPort()) + " / ";
520 if (m_pChannel->midiChannel() == LSCP_MIDI_CHANNEL_ALL)
521 sMidiPortChannel += tr("All");
522 else
523 sMidiPortChannel += QString::number(m_pChannel->midiChannel() + 1);
524 m_ui.MidiPortChannelTextLabel->setText(sMidiPortChannel);
525
526 // Common palette...
527 QPalette pal;
528 const QColor& rgbFore = pal.color(QPalette::Foreground);
529
530 // Instrument status...
531 const int iInstrumentStatus = m_pChannel->instrumentStatus();
532 if (iInstrumentStatus < 0) {
533 pal.setColor(QPalette::Foreground, Qt::red);
534 m_ui.InstrumentStatusTextLabel->setPalette(pal);
535 m_ui.InstrumentStatusTextLabel->setText(
536 tr("ERR%1").arg(iInstrumentStatus));
537 m_iErrorCount++;
538 return false;
539 }
540
541 // All seems normal...
542 pal.setColor(QPalette::Foreground,
543 iInstrumentStatus < 100 ? Qt::yellow : Qt::green);
544 m_ui.InstrumentStatusTextLabel->setPalette(pal);
545 m_ui.InstrumentStatusTextLabel->setText(
546 QString::number(iInstrumentStatus) + '%');
547 m_iErrorCount = 0;
548
549 #ifdef CONFIG_MUTE_SOLO
550 // Mute/Solo button state coloring...
551 const bool bMute = m_pChannel->channelMute();
552 const QColor& rgbButton = pal.color(QPalette::Button);
553 const QColor& rgbButtonText = pal.color(QPalette::ButtonText);
554 pal.setColor(QPalette::Foreground, rgbFore);
555 pal.setColor(QPalette::Button, bMute ? Qt::yellow : rgbButton);
556 pal.setColor(QPalette::ButtonText, bMute ? Qt::darkYellow : rgbButtonText);
557 m_ui.ChannelMutePushButton->setPalette(pal);
558 m_ui.ChannelMutePushButton->setDown(bMute);
559 const bool bSolo = m_pChannel->channelSolo();
560 pal.setColor(QPalette::Button, bSolo ? Qt::cyan : rgbButton);
561 pal.setColor(QPalette::ButtonText, bSolo ? Qt::darkCyan : rgbButtonText);
562 m_ui.ChannelSoloPushButton->setPalette(pal);
563 m_ui.ChannelSoloPushButton->setDown(bSolo);
564 #else
565 m_ui.ChannelMutePushButton->setEnabled(false);
566 m_ui.ChannelSoloPushButton->setEnabled(false);
567 #endif
568
569 // And update the both GUI volume elements;
570 // return success if, and only if, intrument is fully loaded...
571 return updateChannelVolume() && (iInstrumentStatus == 100);
572 }
573
574
575 // Update whole channel usage state.
576 bool ChannelStrip::updateChannelUsage (void)
577 {
578 if (m_pChannel == NULL)
579 return false;
580
581 MainForm *pMainForm = MainForm::getInstance();
582 if (pMainForm->client() == NULL)
583 return false;
584
585 // This only makes sense on fully loaded channels...
586 if (m_pChannel->instrumentStatus() < 100)
587 return false;
588
589 // Get current channel voice count.
590 const int iVoiceCount = ::lscp_get_channel_voice_count(
591 pMainForm->client(), m_pChannel->channelID());
592 // Get current stream count.
593 const int iStreamCount = ::lscp_get_channel_stream_count(
594 pMainForm->client(), m_pChannel->channelID());
595 // Get current channel buffer fill usage.
596 // As benno has suggested this is the percentage usage
597 // of the least filled buffer stream...
598 const int iStreamUsage = ::lscp_get_channel_stream_usage(
599 pMainForm->client(), m_pChannel->channelID());;
600
601 // Update the GUI elements...
602 m_ui.StreamUsageProgressBar->setValue(iStreamUsage);
603 m_ui.StreamVoiceCountTextLabel->setText(
604 QString("%1 / %2").arg(iStreamCount).arg(iVoiceCount));
605
606 // We're clean.
607 return true;
608 }
609
610
611 // Volume change slot.
612 void ChannelStrip::volumeChanged ( int iVolume )
613 {
614 if (m_pChannel == NULL)
615 return;
616
617 // Avoid recursion.
618 if (m_iDirtyChange > 0)
619 return;
620
621 // Convert and clip.
622 float fVolume = (float) iVolume / 100.0f;
623 if (fVolume < 0.001f)
624 fVolume = 0.0f;
625
626 // Update the GUI elements.
627 if (m_pChannel->setVolume(fVolume)) {
628 updateChannelVolume();
629 emit channelChanged(this);
630 }
631 }
632
633
634 // Context menu event handler.
635 void ChannelStrip::contextMenuEvent( QContextMenuEvent *pEvent )
636 {
637 if (m_pChannel == NULL)
638 return;
639
640 // We'll just show up the main form's edit menu (thru qsamplerChannel).
641 m_pChannel->contextMenuEvent(pEvent);
642 }
643
644
645 void ChannelStrip::midiActivityLedOn (void)
646 {
647 m_ui.MidiActivityLabel->setPixmap(*g_pMidiActivityLedOn);
648 m_pMidiActivityTimer->start(100);
649 }
650
651
652 void ChannelStrip::midiActivityLedOff (void)
653 {
654 m_ui.MidiActivityLabel->setPixmap(*g_pMidiActivityLedOff);
655 }
656
657
658 // Error count hackish accessors.
659 void ChannelStrip::resetErrorCount (void)
660 {
661 m_iErrorCount = 0;
662 }
663
664
665 // Channel strip activation/selection.
666 void ChannelStrip::setSelected ( bool bSelected )
667 {
668 if (bSelected) {
669 if (g_pSelectedStrip == this)
670 return;
671 if (g_pSelectedStrip)
672 g_pSelectedStrip->setSelected(false);
673 g_pSelectedStrip = this;
674 } else {
675 if (g_pSelectedStrip == this)
676 g_pSelectedStrip = NULL;
677 }
678
679 QPalette pal;
680 if (bSelected) {
681 const QColor& color = pal.midlight().color();
682 pal.setColor(QPalette::Background, color.dark(150));
683 pal.setColor(QPalette::Foreground, color.light(150));
684 }
685
686 QWidget *pParentWidget = QWidget::parentWidget();
687 if (pParentWidget)
688 pParentWidget->setPalette(pal);
689 }
690
691
692 bool ChannelStrip::isSelected (void) const
693 {
694 return (this == g_pSelectedStrip);
695 }
696
697
698 } // namespace QSampler
699
700
701 // end of qsamplerChannelStrip.cpp

  ViewVC Help
Powered by ViewVC