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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2569 - (show annotations) (download)
Tue May 20 20:04:19 2014 UTC (9 years, 10 months ago) by schoenebeck
File size: 17536 byte(s)
* Added instrument list popup on channel strip which shows up when
  the instrument name is clicked. Allows faster switching among
  instruments of the same file.

1 // qsamplerChannelStrip.cpp
2 //
3 /****************************************************************************
4 Copyright (C) 2004-2014, 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.VolumeSlider,
115 SIGNAL(valueChanged(int)),
116 SLOT(volumeChanged(int)));
117 QObject::connect(m_ui.VolumeSpinBox,
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.FxPushButton,
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 void ChannelStrip::setDisplayFont ( const QFont & font )
241 {
242 m_ui.EngineNameTextLabel->setFont(font);
243 m_ui.MidiPortChannelTextLabel->setFont(font);
244 m_ui.InstrumentNamePushButton->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 if (bDisplayEffect) {
260 QPixmap pm(":/images/displaybg1.png");
261 pal.setBrush(QPalette::Background, QBrush(pm));
262 } else {
263 pal.setColor(QPalette::Background, Qt::black);
264 }
265 m_ui.ChannelInfoFrame->setPalette(pal);
266 m_ui.InstrumentNamePushButton->setPalette(pal);
267 m_ui.StreamVoiceCountTextLabel->setPalette(pal);
268 }
269
270
271 // Maximum volume slider accessors.
272 void ChannelStrip::setMaxVolume ( int iMaxVolume )
273 {
274 m_iDirtyChange++;
275 m_ui.VolumeSlider->setRange(0, iMaxVolume);
276 m_ui.VolumeSpinBox->setRange(0, iMaxVolume);
277 m_iDirtyChange--;
278 }
279
280
281 // Channel setup dialog slot.
282 bool ChannelStrip::channelSetup (void)
283 {
284 if (m_pChannel == NULL)
285 return false;
286
287 // Invoke the channel setup dialog.
288 bool bResult = m_pChannel->channelSetup(this);
289 // Notify that this channel has changed.
290 if (bResult)
291 emit channelChanged(this);
292
293 return bResult;
294 }
295
296
297 // Channel mute slot.
298 bool ChannelStrip::channelMute ( bool bMute )
299 {
300 if (m_pChannel == NULL)
301 return false;
302
303 // Invoke the channel mute method.
304 bool bResult = m_pChannel->setChannelMute(bMute);
305 // Notify that this channel has changed.
306 if (bResult)
307 emit channelChanged(this);
308
309 return bResult;
310 }
311
312
313 // Channel solo slot.
314 bool ChannelStrip::channelSolo ( bool bSolo )
315 {
316 if (m_pChannel == NULL)
317 return false;
318
319 // Invoke the channel solo method.
320 bool bResult = m_pChannel->setChannelSolo(bSolo);
321 // Notify that this channel has changed.
322 if (bResult)
323 emit channelChanged(this);
324
325 return bResult;
326 }
327
328
329 // Channel edit slot.
330 void ChannelStrip::channelEdit (void)
331 {
332 if (m_pChannel == NULL)
333 return;
334
335 m_pChannel->editChannel();
336 }
337
338 bool ChannelStrip::channelFxEdit (void)
339 {
340 MainForm *pMainForm = MainForm::getInstance();
341 if (!pMainForm || !channel())
342 return false;
343
344 pMainForm->appendMessages(QObject::tr("channel fx sends..."));
345
346 bool bResult = false;
347
348 #if CONFIG_FXSEND
349 ChannelFxForm *pChannelFxForm =
350 new ChannelFxForm(channel(), parentWidget());
351 if (pChannelFxForm) {
352 //pChannelForm->setup(this);
353 bResult = pChannelFxForm->exec();
354 delete pChannelFxForm;
355 }
356 #else // CONFIG_FXSEND
357 QMessageBox::critical(this,
358 QSAMPLER_TITLE ": " + tr("Unavailable"),
359 tr("Sorry, QSampler was built without FX send support!\n\n"
360 "(Make sure you have a recent liblscp when recompiling QSampler)"));
361 #endif // CONFIG_FXSEND
362
363 return bResult;
364 }
365
366 // Channel reset slot.
367 bool ChannelStrip::channelReset (void)
368 {
369 if (m_pChannel == NULL)
370 return false;
371
372 // Invoke the channel reset method.
373 bool bResult = m_pChannel->channelReset();
374 // Notify that this channel has changed.
375 if (bResult)
376 emit channelChanged(this);
377
378 return bResult;
379 }
380
381
382 // Update the channel instrument name.
383 bool ChannelStrip::updateInstrumentName ( bool bForce )
384 {
385 if (m_pChannel == NULL)
386 return false;
387
388 // Do we refresh the actual name?
389 if (bForce)
390 m_pChannel->updateInstrumentName();
391
392 // Instrument name...
393 if (m_pChannel->instrumentName().isEmpty()) {
394 if (m_pChannel->instrumentStatus() >= 0) {
395 m_ui.InstrumentNamePushButton->setText(
396 ' ' + Channel::loadingInstrument());
397 } else {
398 m_ui.InstrumentNamePushButton->setText(
399 ' ' + Channel::noInstrumentName());
400 }
401 } else {
402 m_ui.InstrumentNamePushButton->setText(
403 ' ' + m_pChannel->instrumentName());
404 }
405
406 bool bShowInstrumentPopup = false;
407
408 // Instrument list popup (for fast switching among sounds of the same file)
409 if (!m_pChannel->instrumentFile().isEmpty()) {
410 QStringList instruments = Channel::getInstrumentList(
411 m_pChannel->instrumentFile(), true
412 );
413 if (!instruments.isEmpty()) {
414 bShowInstrumentPopup = true;
415 if (!m_instrumentListPopupMenu) {
416 m_instrumentListPopupMenu = new QMenu(m_ui.InstrumentNamePushButton);
417 m_instrumentListPopupMenu->setTitle(tr("Instruments"));
418 m_instrumentListPopupMenu->setMinimumWidth(118); // for cosmetical reasons, should have at least the width of the instrument name label
419 m_ui.InstrumentNamePushButton->setMenu(m_instrumentListPopupMenu);
420 QObject::connect(
421 m_instrumentListPopupMenu, SIGNAL(triggered(QAction*)),
422 this, SLOT(instrumentListPopupItemClicked(QAction*))
423 );
424 } else m_instrumentListPopupMenu->clear();
425
426 for (int i = 0; i < instruments.size(); ++i) {
427 QAction* action =
428 m_instrumentListPopupMenu->addAction(instruments.at(i));
429 action->setData(i);
430 if (i == m_pChannel->instrumentNr()) {
431 action->setCheckable(true);
432 action->setChecked(true);
433 }
434 }
435 }
436 }
437
438 if (!bShowInstrumentPopup && m_instrumentListPopupMenu) {
439 delete m_instrumentListPopupMenu;
440 m_instrumentListPopupMenu = NULL;
441 }
442
443 return true;
444 }
445
446 void ChannelStrip::instrumentListPopupItemClicked (QAction* action)
447 {
448 if (!action) return;
449
450 QVariant data = action->data();
451 if (data.isValid() && !m_pChannel->instrumentFile().isEmpty()) {
452 m_pChannel->loadInstrument(m_pChannel->instrumentFile(), data.toInt());
453 emit channelChanged(this);
454 }
455 }
456
457 // Do the dirty volume change.
458 bool ChannelStrip::updateChannelVolume (void)
459 {
460 if (m_pChannel == NULL)
461 return false;
462
463 // Convert...
464 int iVolume = ::lroundf(100.0f * m_pChannel->volume());
465 // And clip...
466 if (iVolume < 0)
467 iVolume = 0;
468
469 // Flag it here, to avoid infinite recursion.
470 m_iDirtyChange++;
471 m_ui.VolumeSlider->setValue(iVolume);
472 m_ui.VolumeSpinBox->setValue(iVolume);
473 m_iDirtyChange--;
474
475 return true;
476 }
477
478
479 // Update whole channel info state.
480 bool ChannelStrip::updateChannelInfo (void)
481 {
482 if (m_pChannel == NULL)
483 return false;
484
485 // Check for error limit/recycle...
486 if (m_iErrorCount > QSAMPLER_ERROR_LIMIT)
487 return true;
488
489 // Update strip caption.
490 QString sText = m_pChannel->channelName();
491 setWindowTitle(sText);
492 m_ui.ChannelSetupPushButton->setText('&' + sText);
493
494 // Check if we're up and connected.
495 MainForm* pMainForm = MainForm::getInstance();
496 if (pMainForm->client() == NULL)
497 return false;
498
499 // Read actual channel information.
500 m_pChannel->updateChannelInfo();
501
502 // Engine name...
503 if (m_pChannel->engineName().isEmpty()) {
504 m_ui.EngineNameTextLabel->setText(
505 ' ' + Channel::noEngineName());
506 } else {
507 m_ui.EngineNameTextLabel->setText(
508 ' ' + m_pChannel->engineName());
509 }
510
511 // Instrument name...
512 updateInstrumentName(false);
513
514 // MIDI Port/Channel...
515 QString sMidiPortChannel = QString::number(m_pChannel->midiPort()) + " / ";
516 if (m_pChannel->midiChannel() == LSCP_MIDI_CHANNEL_ALL)
517 sMidiPortChannel += tr("All");
518 else
519 sMidiPortChannel += QString::number(m_pChannel->midiChannel() + 1);
520 m_ui.MidiPortChannelTextLabel->setText(sMidiPortChannel);
521
522 // Common palette...
523 QPalette pal;
524 const QColor& rgbFore = pal.color(QPalette::Foreground);
525
526 // Instrument status...
527 int iInstrumentStatus = m_pChannel->instrumentStatus();
528 if (iInstrumentStatus < 0) {
529 pal.setColor(QPalette::Foreground, Qt::red);
530 m_ui.InstrumentStatusTextLabel->setPalette(pal);
531 m_ui.InstrumentStatusTextLabel->setText(
532 tr("ERR%1").arg(iInstrumentStatus));
533 m_iErrorCount++;
534 return false;
535 }
536 // All seems normal...
537 pal.setColor(QPalette::Foreground,
538 iInstrumentStatus < 100 ? Qt::yellow : Qt::green);
539 m_ui.InstrumentStatusTextLabel->setPalette(pal);
540 m_ui.InstrumentStatusTextLabel->setText(
541 QString::number(iInstrumentStatus) + '%');
542 m_iErrorCount = 0;
543
544 #ifdef CONFIG_MUTE_SOLO
545 // Mute/Solo button state coloring...
546 bool bMute = m_pChannel->channelMute();
547 const QColor& rgbButton = pal.color(QPalette::Button);
548 const QColor& rgbButtonText = pal.color(QPalette::ButtonText);
549 pal.setColor(QPalette::Foreground, rgbFore);
550 pal.setColor(QPalette::Button, bMute ? Qt::yellow : rgbButton);
551 pal.setColor(QPalette::ButtonText, bMute ? Qt::darkYellow : rgbButtonText);
552 m_ui.ChannelMutePushButton->setPalette(pal);
553 m_ui.ChannelMutePushButton->setDown(bMute);
554 bool bSolo = m_pChannel->channelSolo();
555 pal.setColor(QPalette::Button, bSolo ? Qt::cyan : rgbButton);
556 pal.setColor(QPalette::ButtonText, bSolo ? Qt::darkCyan : rgbButtonText);
557 m_ui.ChannelSoloPushButton->setPalette(pal);
558 m_ui.ChannelSoloPushButton->setDown(bSolo);
559 #else
560 m_ui.ChannelMutePushButton->setEnabled(false);
561 m_ui.ChannelSoloPushButton->setEnabled(false);
562 #endif
563
564 // And update the both GUI volume elements;
565 // return success if, and only if, intrument is fully loaded...
566 return updateChannelVolume() && (iInstrumentStatus == 100);
567 }
568
569
570 // Update whole channel usage state.
571 bool ChannelStrip::updateChannelUsage (void)
572 {
573 if (m_pChannel == NULL)
574 return false;
575
576 MainForm *pMainForm = MainForm::getInstance();
577 if (pMainForm->client() == NULL)
578 return false;
579
580 // This only makes sense on fully loaded channels...
581 if (m_pChannel->instrumentStatus() < 100)
582 return false;
583
584 // Get current channel voice count.
585 int iVoiceCount = ::lscp_get_channel_voice_count(
586 pMainForm->client(), m_pChannel->channelID());
587 // Get current stream count.
588 int iStreamCount = ::lscp_get_channel_stream_count(
589 pMainForm->client(), m_pChannel->channelID());
590 // Get current channel buffer fill usage.
591 // As benno has suggested this is the percentage usage
592 // of the least filled buffer stream...
593 int iStreamUsage = ::lscp_get_channel_stream_usage(
594 pMainForm->client(), m_pChannel->channelID());;
595
596 // Update the GUI elements...
597 m_ui.StreamUsageProgressBar->setValue(iStreamUsage);
598 m_ui.StreamVoiceCountTextLabel->setText(
599 QString("%1 / %2").arg(iStreamCount).arg(iVoiceCount));
600
601 // We're clean.
602 return true;
603 }
604
605
606 // Volume change slot.
607 void ChannelStrip::volumeChanged ( int iVolume )
608 {
609 if (m_pChannel == NULL)
610 return;
611
612 // Avoid recursion.
613 if (m_iDirtyChange > 0)
614 return;
615
616 // Convert and clip.
617 float fVolume = (float) iVolume / 100.0f;
618 if (fVolume < 0.001f)
619 fVolume = 0.0f;
620
621 // Update the GUI elements.
622 if (m_pChannel->setVolume(fVolume)) {
623 updateChannelVolume();
624 emit channelChanged(this);
625 }
626 }
627
628
629 // Context menu event handler.
630 void ChannelStrip::contextMenuEvent( QContextMenuEvent *pEvent )
631 {
632 if (m_pChannel == NULL)
633 return;
634
635 // We'll just show up the main form's edit menu (thru qsamplerChannel).
636 m_pChannel->contextMenuEvent(pEvent);
637 }
638
639
640 void ChannelStrip::midiActivityLedOn (void)
641 {
642 m_ui.MidiActivityLabel->setPixmap(*g_pMidiActivityLedOn);
643 m_pMidiActivityTimer->start(100);
644 }
645
646
647 void ChannelStrip::midiActivityLedOff (void)
648 {
649 m_ui.MidiActivityLabel->setPixmap(*g_pMidiActivityLedOff);
650 }
651
652
653 // Error count hackish accessors.
654 void ChannelStrip::resetErrorCount (void)
655 {
656 m_iErrorCount = 0;
657 }
658
659
660 // Channel strip activation/selection.
661 void ChannelStrip::setSelected ( bool bSelected )
662 {
663 if (bSelected) {
664 if (g_pSelectedStrip == this)
665 return;
666 if (g_pSelectedStrip)
667 g_pSelectedStrip->setSelected(false);
668 g_pSelectedStrip = this;
669 } else {
670 if (g_pSelectedStrip == this)
671 g_pSelectedStrip = NULL;
672 }
673
674 QPalette pal;
675 if (bSelected) {
676 const QColor& color = pal.midlight().color();
677 pal.setColor(QPalette::Background, color.dark(150));
678 pal.setColor(QPalette::Foreground, color.light(150));
679 }
680 QWidget::setPalette(pal);
681 }
682
683
684 bool ChannelStrip::isSelected (void) const
685 {
686 return (this == g_pSelectedStrip);
687 }
688
689
690 } // namespace QSampler
691
692
693 // end of qsamplerChannelStrip.cpp

  ViewVC Help
Powered by ViewVC