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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2108 - (show annotations) (download)
Thu Jul 15 08:03:32 2010 UTC (13 years, 9 months ago) by capela
File size: 15699 byte(s)
* Sampler channel and instrument file requester support for
  other than GIG instrument files (*gig *.dls) has been added,
  now also allowing for SFZ instrument files (*.sfz) loading.

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

  ViewVC Help
Powered by ViewVC