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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2036 - (show annotations) (download)
Wed Jan 6 09:52:32 2010 UTC (14 years, 2 months ago) by capela
File size: 15407 byte(s)
* MIDI activity indicator on channel strips is now implemented as
  a flashing green LED, instead of a lame solid background :).

- New year's bumping.

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

  ViewVC Help
Powered by ViewVC