1 /*
2 Copyright (C) 2008, 2009 Andres Cabrera
3 mantaraya36@gmail.com
4
5 This file is part of CsoundQt.
6
7 CsoundQt is free software; you can redistribute it
8 and/or modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
11
12 CsoundQt 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 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with Csound; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 02111-1307 USA
21 */
22
23 #include "qutegraph.h"
24 #include "curve.h"
25 #include <cmath>
26 #include <QPalette>
27
28
29 #include <QColorDialog>
30
31
32 enum CsoundEngineStatus {
33 Running=0,
34 UserDataNotSet,
35 CsoundInstanceNotSet,
36 EngineNotSet,
37 NotRunning
38 };
39
40
csoundEngineStatus(CsoundUserData * ud)41 inline CsoundEngineStatus csoundEngineStatus(CsoundUserData *ud) {
42 if(ud == nullptr) {
43 return CsoundEngineStatus::UserDataNotSet;
44 }
45 if(ud->csound == nullptr) {
46 return CsoundEngineStatus::CsoundInstanceNotSet;
47 }
48 if(ud->csEngine == nullptr) {
49 return CsoundEngineStatus::EngineNotSet;
50 }
51 if(!ud->csEngine->isRunning()) {
52 return CsoundEngineStatus::NotRunning;
53 }
54 return CsoundEngineStatus::Running;
55 }
56
57
58 // -----------------------------------------------------------------------------------------
59
QuteGraph(QWidget * parent)60 QuteGraph::QuteGraph(QWidget *parent) : QuteWidget(parent)
61 {
62 m_widget = new StackedLayoutWidget(this);
63 m_widget->show();
64 // m_widget->setAutoFillBackground(true);
65 m_widget->setMouseTracking(true); // Necessary to pass mouse tracking to widget panel for _MouseX channels
66 m_widget->setContextMenuPolicy(Qt::NoContextMenu);
67 m_numticksY = 6;
68 m_label = new QLabel(this);
69 QPalette palette = m_widget->palette();
70 palette.setColor(QPalette::WindowText, QColor(150, 150, 150));
71 m_label->setPalette(palette);
72 m_label->setText("");
73 m_label->setFont(QFont({"Helvetica", 7}));
74 m_label->move(120, -4);
75 m_label->resize(500, 25);
76 m_pageComboBox = new QComboBox(this);
77 m_pageComboBox->setMinimumWidth(120);
78 m_pageComboBox->setMaximumHeight(14);
79 m_pageComboBox->setFont(QFont({"Sans", 7}));
80 m_pageComboBox->setFocusPolicy(Qt::NoFocus);
81 m_pageComboBox->setStyleSheet("QComboBox QAbstractItemView"
82 "{ min-width: 150px; }");
83 // m_pageComboBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
84 m_pageComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
85 m_label->setFocusPolicy(Qt::NoFocus);
86 m_drawGrid = true;
87 m_drawTableInfo = true;
88 m_showScrollbars = true;
89 canFocus(false);
90 connect(m_pageComboBox, SIGNAL(currentIndexChanged(int)),
91 this, SLOT(indexChanged(int)));
92 polygons.clear();
93
94 QPalette Pal(this->palette());
95 // set black background
96 Pal.setColor(QPalette::Background, Qt::black);
97 this->setAutoFillBackground(true);
98 this->setPalette(Pal);
99
100 // Default properties
101 setProperty("QCS_zoomx", 1.0);
102 setProperty("QCS_zoomy", 1.0);
103 setProperty("QCS_dispx", 1.0);
104 setProperty("QCS_dispy", 1.0);
105 setProperty("QCS_modex", "auto");
106 setProperty("QCS_modey", "auto");
107 setProperty("QCS_showSelector", true);
108 setProperty("QCS_showGrid", true);
109 setProperty("QCS_showTableInfo", true);
110 setProperty("QCS_showScrollbars", true);
111 setProperty("QCS_enableTables", true);
112 setProperty("QCS_enableDisplays", true);
113 m_enableTables = true;
114 m_enableDisplays = true;
115 setProperty("QCS_all", true);
116
117 m_showPeak = false;
118 m_showPeakCenterFrequency = 1000.0;
119 m_showPeakRelativeBandwidth = 0.25;
120 m_lastPeakFreq = 0;
121 m_frozen = false;
122 m_getPeakChannel = "";
123 m_peakChannelPtr = nullptr;
124 }
125
~QuteGraph()126 QuteGraph::~QuteGraph()
127 {
128 }
129
getWidgetLine()130 QString QuteGraph::getWidgetLine()
131 {
132 // Extension to MacCsound: type of graph (table, ftt, scope), value (which hold the index of the
133 // table displayed) zoom and channel name
134 // channel number is unused in QuteGraph, but selects channel for scope
135 #ifdef USE_WIDGET_MUTEX
136 widgetLock.lockForRead();
137 #endif
138 QString line = "ioGraph {" + QString::number(x()) + ", " + QString::number(y()) + "} ";
139 line += "{"+ QString::number(width()) +", "+ QString::number(height()) +"} table ";
140 line += QString::number(m_value, 'f', 6) + " ";
141 line += QString::number(property("QCS_zoomx").toDouble(), 'f', 6) + " ";
142 line += m_channel;
143 // qDebug("QuteGraph::getWidgetLine(): %s", line.toStdString().c_str());
144 #ifdef USE_WIDGET_MUTEX
145 widgetLock.unlock();
146 #endif
147 return line;
148 }
149
getWidgetXmlText()150 QString QuteGraph::getWidgetXmlText()
151 {
152 // Graphs are not implemented in blue
153 xmlText = "";
154 QXmlStreamWriter s(&xmlText);
155 createXmlWriter(s);
156 #ifdef USE_WIDGET_MUTEX
157 widgetLock.lockForRead();
158 #endif
159
160 s.writeTextElement("value", QString::number((int)m_value));
161 s.writeTextElement("objectName2", m_channel2);
162 s.writeTextElement("zoomx", QString::number(property("QCS_zoomx").toDouble(), 'f', 8));
163 s.writeTextElement("zoomy", QString::number(property("QCS_zoomy").toDouble(), 'f', 8));
164 s.writeTextElement("dispx", QString::number(property("QCS_dispx").toDouble(), 'f', 8));
165 s.writeTextElement("dispy", QString::number(property("QCS_dispy").toDouble(), 'f', 8));
166 s.writeTextElement("modex", property("QCS_modex").toString());
167 s.writeTextElement("modey", property("QCS_modey").toString());
168 s.writeTextElement("showSelector",
169 property("QCS_showSelector").toBool() ? "true" : "false");
170 s.writeTextElement("showGrid", property("QCS_showGrid").toBool() ? "true" : "false");
171 s.writeTextElement("showTableInfo",
172 property("QCS_showTableInfo").toBool() ? "true" : "false");
173 s.writeTextElement("showScrollbars",
174 property("QCS_showScrollbars").toBool() ? "true" : "false");
175 s.writeTextElement("enableTables",
176 property("QCS_enableTables").toBool() ? "true" : "false");
177 s.writeTextElement("enableDisplays", m_enableDisplays ? "true" : "false");
178 s.writeTextElement("all", property("QCS_all").toBool() ? "true" : "false");
179 s.writeEndElement();
180 #ifdef USE_WIDGET_MUTEX
181 widgetLock.unlock();
182 #endif
183 return xmlText;
184 }
185
186
getWidgetType()187 QString QuteGraph::getWidgetType() {
188 return QString("BSBGraph");
189 }
190
setWidgetGeometry(int x,int y,int width,int height)191 void QuteGraph::setWidgetGeometry(int x,int y,int width,int height)
192 {
193 QuteWidget::setWidgetGeometry(x,y,width, height);
194 static_cast<StackedLayoutWidget *>(m_widget)->setWidgetGeometry(0,0,width, height);
195 int index = static_cast<StackedLayoutWidget *>(m_widget)->currentIndex();
196 changeCurve(-2);
197
198 if (index < 0)
199 return;
200 // changeCurve(index);
201 }
202
mousePressEvent(QMouseEvent * event)203 void QuteGraph::mousePressEvent(QMouseEvent *event) {
204 int index = (int)m_value;
205 switch(graphtypes[index]) {
206 case GraphType::GRAPH_SPECTRUM: {
207 if(event->button() & Qt::LeftButton) {
208 // turn on peak detection
209 m_showPeakTemp = true;
210 m_spectrumPeakMarkers[index]->setVisible(true);
211 m_spectrumPeakTexts[index]->setVisible(true);
212 m_mouseDragging = true;
213 auto view = getView(index);
214 auto peakIndex = view->mapToScene(event->x(), 0).x();
215 auto curve = curves[index];
216 auto freq = peakIndex / curve->get_size() * this->m_ud->sampleRate*0.5;
217 m_showPeakTempFrequency = freq;
218 return;
219 }
220 break;
221 }
222 default:
223 break;
224 }
225 }
226
mouseReleased()227 void QuteGraph::mouseReleased() {
228 m_mouseDragging = false;
229 if(!m_showPeak ) {
230 m_lastPeakFreq = 0;
231 m_lastTextMarkerY = 0;
232 m_lastTextMarkerX = 0;
233 }
234 m_showPeakTemp = false;
235 int index = (int)m_value;
236 m_spectrumPeakTexts[index]->setVisible(m_showPeak);
237 auto scene = getView(index)->scene();
238 m_spectrumPeakMarkers[index]->setVisible(m_showPeak);
239 }
240
241 /*
242 void QuteGraph::mouseMoveEvent(QMouseEvent *event) {
243 int index = (int)m_value;
244 switch(graphtypes[index]) {
245 case GraphType::GRAPH_SPECTRUM: {
246 if(!m_mouseDragging)
247 return;
248 auto peakIndex = getView(index)->mapToScene(event->x(), 0).x();
249 auto curveSize = curves[index]->get_size();
250 m_showPeakTempFrequency = peakIndex / curveSize * this->m_ud->sampleRate*0.5;
251 return;
252 }
253 default:
254 break;
255 }
256 }
257 */
258
259
keyPressEvent(QKeyEvent * event)260 void QuteGraph::keyPressEvent(QKeyEvent *event) {
261 bool flag;
262 int index = m_value;
263 auto graphtype = graphtypes[index];
264 switch(event->key()) {
265 case Qt::Key_F:
266 if(graphtype != GraphType::GRAPH_SPECTRUM)
267 return;
268 freezeSpectrum(!m_frozen);
269 event->accept();
270 break;
271 case Qt::Key_S:
272 flag = !property("QCS_showSelector").toBool();
273 setProperty("QCS_showSelector", flag?"true":"false");
274 if(flag)
275 m_pageComboBox->show();
276 else
277 m_pageComboBox->hide();
278 event->accept();
279 break;
280 case Qt::Key_G:
281 flag = !property("QCS_showGrid").toBool();
282 setProperty("QCS_showGrid", flag?"true":"false");
283 m_drawGrid = flag;
284 event->accept();
285 break;
286 case Qt::Key_C:
287 flag = !property("QCS_showTableInfo").toBool();
288 setProperty("QCS_showTableInfo", flag?"true":"false");
289 m_drawTableInfo = flag;
290 if(flag)
291 m_label->show();
292 else
293 m_label->hide();
294 event->accept();
295 break;
296 case Qt::Key_Plus:
297 setProperty("QCS_zoomx", property("QCS_zoomx").toDouble()*2);
298 applyInternalProperties();
299 event->accept();
300 break;
301 case Qt::Key_Minus:
302 setProperty("QCS_zoomx", qMax(1.0, property("QCS_zoomx").toDouble()*0.5));
303 applyInternalProperties();
304 event->accept();
305 break;
306 case Qt::Key_Z:
307 flag = !property("QCS_showScrollbars").toBool();
308 setProperty("QCS_showScrollbars", flag);
309 showScrollbars(flag);
310 event->accept();
311 break;
312 case Qt::Key_H:
313 QMessageBox mb;
314 mb.setText(tr("<span style=\"font-size : 11pt\">"
315 "<ul>"
316 "<li><code>+</code> : zoom in"
317 "<li><code>-</code> : zoom out"
318 "<li><code>F</code> : toggle freeze (spectrum)"
319 "<li><code>S</code> : toggle Selector"
320 "<li><code>G</code> : toggle Grid"
321 "<li><code>C</code> : toggle table information"
322 "<li><code>Z</code> : toggle Scrollbars"
323 "</ul>"
324 "</span>"
325 ));
326 mb.setWindowTitle("Graph Shortcuts");
327 mb.exec();
328 event->accept();
329 break;
330 }
331 }
332
333
setValue(double value)334 void QuteGraph::setValue(double value)
335 {
336 QuteWidget::setValue(value);
337 // m_value2 = this->getTableNumForIndex(value);
338 /*
339 if(value < 0) {
340 int tabnum = -((int)value);
341 int index = this->getIndexForTableNum(tabnum);
342 QuteWidget::setValue(index);
343 } else {
344 QuteWidget::setValue(value);
345 }
346 */
347
348 }
349
setValue(QString text)350 void QuteGraph::setValue(QString text)
351 {
352 bool ok;
353 auto parts = text.splitRef(' ', QString::SkipEmptyParts);
354 if(parts[0] == "@set") {
355 bool ok;
356 int index = parts[1].toInt(&ok);
357 if(parts.size() != 2 || !ok) {
358 qDebug() << "@set: value error";
359 qDebug() << "@set syntax: @set <curveindex:int>";
360 return;
361 }
362 if(index >= 0 && index < curves.size())
363 this->setValue(index);
364 }
365 else if(parts[0] == "@find") {
366 // @find type text
367 // type: one of fft, audio, ftable
368 // Example: @find fft asignal
369 qDebug() << "@find " << parts;
370 if(parts.size() != 3) {
371 qDebug() << "@find: Wrong number of arguments";
372 qDebug() << "syntax: @find <kind:str> <text:str>";
373 qDebug() << " * kind: one of fft, audio, ftable";
374 qDebug() << " * text: a text to match against the caption";
375 return;
376 }
377 int index;
378 if(parts[1] == "fft") {
379 index = findCurve(CURVE_SPECTRUM, parts[2].toString());
380 }
381 else if (parts[1] == "audio") {
382 index = findCurve(CURVE_AUDIOSIGNAL, parts[2].toString());
383 }
384 else if (parts[1] == "table") {
385 int tabnum = parts[2].toInt(&ok);
386 if(!ok) {
387 qDebug()<<"@find table syntax: @find table <tablenumber:int>";
388 return;
389 }
390 index = this->getIndexForTableNum(tabnum);
391 }
392 else {
393 qDebug() << "@find: graph type should be one of 'table', 'fft' or 'audio', got"
394 << parts[1];
395 return;
396 }
397 if(index >= 0 && index < curves.size())
398 this->setValue(index);
399 else {
400 qDebug() << "@find: graph not found";
401 return;
402 }
403 }
404 else if(parts[0] == "@freeze") {
405 int index = m_value;
406 if(graphtypes[index] != GraphType::GRAPH_SPECTRUM)
407 return;
408 bool ok;
409 int status = parts[1].toInt(&ok);
410 if(status != 0 && status != 1) {
411 qDebug() << "Syntax: @freeze <status:int> (0 or 1)";
412 return;
413 }
414 freezeSpectrum(status);
415 }
416 else if (parts[0] == "@showPeak") {
417 if(parts.size() == 2) {
418 if(parts[1] == "true" || parts[1] == "1") {
419 m_showPeak = true;
420 }
421 else if (parts[1] == "false" || parts[1] == "0") {
422 m_showPeak = false;
423 } else {
424 bool ok;
425 double freq = parts[1].toDouble(&ok);
426 if(!ok) {
427 qDebug() << "@showPeak: expected a frequency, got" << parts[1];
428 return;
429 }
430 m_showPeak = true;
431 m_showPeakCenterFrequency = freq;
432 }
433 }
434 else if (parts.size() == 3) {
435 // @showPeak freq bw
436 bool ok;
437 double freq = parts[1].toDouble(&ok);
438 if(!ok) {
439 qDebug() << "@showPeak: expected a frequency, got" << parts[1];
440 return;
441 }
442 double bw = parts[2].toDouble(&ok);
443 if(!ok) {
444 qDebug() << "@showPeak: expected a bandwidth as third arg, got" << parts[2];
445 return;
446 }
447 m_showPeak = true;
448 if(freq > 0)
449 m_showPeakCenterFrequency = freq;
450 m_showPeakRelativeBandwidth = bw;
451 }
452 else {
453 qDebug() << "@showPeak syntax:";
454 qDebug() << " @showPeak false : turn off peak display";
455 qDebug() << " @showPeak 0 : turn off peak display";
456 qDebug() << " @showPeak true : turn on peak display";
457 qDebug() << " @showPeak 1 : turn on peak display";
458 qDebug() << " @showPeak <freq:double> : turn on peak display, set freq";
459 qDebug() << " @showPeak <freq:double> <bandwidth:double>";
460 qDebug() << " (use freq=0 to only set bandwidth)";
461 return;
462 }
463 }
464 else if(parts[0] == "@getPeak") {
465 m_getPeakChannel = parts[1].toString();
466 qDebug() << "@getPeak channel: " << m_getPeakChannel;
467 MYFLT *ptr;
468 csoundGetChannelPtr(m_ud->csound, &ptr,
469 m_getPeakChannel.toLocal8Bit().constData(),
470 CSOUND_INPUT_CHANNEL | CSOUND_CONTROL_CHANNEL);
471 m_peakChannelPtr = ptr;
472 }
473 else {
474 qDebug() << "Command" << text << "not found";
475 }
476 }
477
478
refreshWidget()479 void QuteGraph::refreshWidget()
480 {
481 bool needsUpdate = false;
482 #ifdef USE_WIDGET_MUTEX
483 widgetLock.lockForRead();
484 #endif
485 int index = 0;
486 if (m_valueChanged) {
487 index = (int) m_value;
488 if(index < 0) {
489 m_value2 = -index;
490 } else {
491 m_value2 = getTableNumForIndex(index);
492 }
493 m_value2Changed = false;
494 m_valueChanged = false;
495 needsUpdate = true;
496 }
497 else if (m_value2Changed) {
498 index = getIndexForTableNum((int)m_value2);
499 if (index >= 0) {
500 m_value = index;
501 // m_valueChanged = false;
502 }
503 m_value2Changed = false;
504 needsUpdate = true;
505 }
506 #ifdef USE_WIDGET_MUTEX
507 widgetLock.unlock(); // unlock
508 #endif
509 if (needsUpdate) {
510 if (index < 0) {
511 index = getIndexForTableNum(-index);
512 }
513 if (index < 0 ||
514 index >= curves.size() ||
515 curves[index]->get_caption().isEmpty()) {
516 // Don't show if curve has no name. Is this likely?
517 return;
518 }
519 // m_pageComboBox->blockSignals(true);
520 // m_pageComboBox->setCurrentIndex(index);
521 // m_pageComboBox->blockSignals(false);
522 changeCurve(index);
523
524 }
525 // QComboBox *cb = this->m_pageComboBox;
526 // cb->move(this->width() - cb->width(), this->height()-cb->height());
527 }
528
createPropertiesDialog()529 void QuteGraph::createPropertiesDialog()
530 {
531 QuteWidget::createPropertiesDialog();
532 dialog->setWindowTitle("Graph");
533
534 channelLabel->setText("Index Channel name =");
535 channelLabel->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
536 // nameLineEdit->setText(getChannelName());
537
538 QLabel *label = new QLabel(dialog);
539 label = new QLabel(dialog);
540 label->setText("F-table Channel name =");
541 layout->addWidget(label, 4, 0, Qt::AlignRight|Qt::AlignVCenter);
542 name2LineEdit = new QLineEdit(dialog);
543 name2LineEdit->setText(getChannel2Name());
544 name2LineEdit->setMinimumWidth(320);
545 layout->addWidget(name2LineEdit, 4,1,1,3, Qt::AlignLeft|Qt::AlignVCenter);
546
547 label = new QLabel(dialog);
548 label->setText("Zoom X");
549 layout->addWidget(label, 8, 0, Qt::AlignRight|Qt::AlignVCenter);
550 zoomxBox = new QDoubleSpinBox(dialog);
551 zoomxBox->setRange(0.1, 10.0);
552 zoomxBox->setDecimals(1);
553 zoomxBox->setSingleStep(0.1);
554 layout->addWidget(zoomxBox, 8, 1, Qt::AlignLeft|Qt::AlignVCenter);
555
556 label = new QLabel(dialog);
557 label->setText("Zoom Y");
558 layout->addWidget(label, 8, 2, Qt::AlignRight|Qt::AlignVCenter);
559 zoomyBox = new QDoubleSpinBox(dialog);
560 zoomyBox->setRange(0.1, 10.0);
561 zoomyBox->setDecimals(1);
562 zoomyBox->setSingleStep(0.1);
563 layout->addWidget(zoomyBox, 8, 3, Qt::AlignLeft|Qt::AlignVCenter);
564
565 acceptTablesCheckBox = new QCheckBox(dialog);
566 acceptTablesCheckBox->setText("Enable tables");
567 acceptTablesCheckBox->setToolTip("Enable the display of tables. Each time a table is "
568 "creted it will be made available to be selected.\n"
569 "NB: modifications to a table will not be shown in the "
570 "graph, use a TablePlot widget for that.\n"
571 "NB2: If you plan to use the Graph widget to display"
572 "spectra or signals, uncheck this option");
573 acceptTablesCheckBox->setChecked(property("QCS_enableTables").toBool());
574 layout->addWidget(acceptTablesCheckBox, 9, 0, Qt::AlignLeft|Qt::AlignVCenter);
575
576 acceptDisplaysCheckBox = new QCheckBox(dialog);
577 acceptDisplaysCheckBox->setText("Enable Displays");
578 acceptDisplaysCheckBox->setToolTip("Enable displaying audio signals/spectra. Check this"
579 "if you plan to use the graph widget to display audio"
580 "signals and spectra using the opcodes display/dispfft");
581 acceptDisplaysCheckBox->setChecked(property("QCS_enableDisplays").toBool());
582 layout->addWidget(acceptDisplaysCheckBox, 9, 1, Qt::AlignLeft|Qt::AlignVCenter);
583
584 showSelectorCheckBox = new QCheckBox(dialog);
585 showSelectorCheckBox->setText("Show Selector");
586 showSelectorCheckBox->setChecked(property("QCS_showSelector").toBool());
587 layout->addWidget(showSelectorCheckBox, 10, 0, Qt::AlignLeft|Qt::AlignVCenter);
588
589 showGridCheckBox = new QCheckBox(dialog);
590 showGridCheckBox->setText("Show Grid");
591 showGridCheckBox->setChecked(property("QCS_showGrid").toBool());
592 showGridCheckBox->setToolTip("Show the grid. Has effect only for spectral graphs");
593 layout->addWidget(showGridCheckBox, 10, 1, Qt::AlignLeft|Qt::AlignVCenter);
594
595 showTableInfoCheckBox = new QCheckBox(dialog);
596 showTableInfoCheckBox->setText("Show Table Information");
597 showTableInfoCheckBox->setCheckState(
598 property("QCS_showTableInfo").toBool()?Qt::Checked:Qt::Unchecked);
599 showTableInfoCheckBox->setToolTip("Show the grid. Has effect only for spectral graphs");
600 layout->addWidget(showTableInfoCheckBox, 11, 0, Qt::AlignLeft|Qt::AlignVCenter);
601
602 showScrollbarsCheckBox = new QCheckBox(dialog);
603 showScrollbarsCheckBox->setText("Show Scrollbars");
604 showScrollbarsCheckBox->setChecked(property("QCS_showScrollbars").toBool());
605 layout->addWidget(showScrollbarsCheckBox, 11, 1, Qt::AlignLeft|Qt::AlignVCenter);
606
607 #ifdef USE_WIDGET_MUTEX
608 widgetLock.lockForRead();
609 #endif
610 zoomxBox->setValue(property("QCS_zoomx").toDouble());
611 zoomyBox->setValue(property("QCS_zoomy").toDouble());
612 #ifdef USE_WIDGET_MUTEX
613 widgetLock.unlock();
614 #endif
615 // channelLabel->hide();
616 // nameLineEdit->hide();
617 }
618
applyProperties()619 void QuteGraph::applyProperties()
620 {
621 #ifdef USE_WIDGET_MUTEX
622 widgetLock.lockForWrite();
623 #endif
624 setProperty("QCS_objectName2", name2LineEdit->text());
625 setProperty("QCS_zoomx", zoomxBox->value());
626 setProperty("QCS_zoomy", zoomyBox->value());
627 setProperty("QCS_dispx", 1);
628 setProperty("QCS_dispy", 1);
629 setProperty("QCS_modex", "lin");
630 setProperty("QCS_modey", "lin");
631 setProperty("QCS_all", true);
632 setProperty("QCS_showSelector", showSelectorCheckBox->checkState());
633 setProperty("QCS_showGrid", showGridCheckBox->checkState());
634 setProperty("QCS_showScrollbars", showScrollbarsCheckBox->isChecked());
635 setProperty("QCS_enableTables", acceptTablesCheckBox->isChecked());
636 setProperty("QCS_enableDisplays", acceptDisplaysCheckBox->isChecked());
637
638 m_enableTables = acceptTablesCheckBox->isChecked();
639 m_enableDisplays = acceptDisplaysCheckBox->isChecked();
640
641 showTableInfo(showTableInfoCheckBox->checkState());
642
643 #ifdef USE_WIDGET_MUTEX
644 widgetLock.unlock();
645 #endif
646 QuteWidget::applyProperties();
647 }
648
649
findCurve(CurveType type,QString text)650 int QuteGraph::findCurve(CurveType type, QString text) {
651 // returns the index or -1 if not found
652 for (int i = 0; i < curves.size(); i++) {
653 if(curves[i]->get_type() != type)
654 continue;
655 QString caption = curves[i]->get_caption();
656 if (caption.contains(text)) {
657 return i;
658 }
659 }
660 return -1;
661 }
662
changeCurve(int index)663 void QuteGraph::changeCurve(int index)
664 {
665 if(curves.size() <= 0)
666 return;
667
668 int origRequest = index;
669 StackedLayoutWidget *stacked = static_cast<StackedLayoutWidget *>(m_widget);
670 if (index == -1) { // goto last curve
671 index = stacked->count() - 1;
672 }
673 else if (index == -2) { // update curve but don't change which
674 if (m_value < 0)
675 index = getIndexForTableNum(-m_value);
676 else
677 index = (int) m_value;
678 } else if (index >= stacked->count()) {
679 qDebug() << "changeCurve: index out of range. Num indices:"<<stacked->size();
680 return;
681 }
682 else if (stacked->currentIndex() == index) {
683 return;
684 } else {
685 // change curve
686 auto view = stacked->currentWidget();
687 view->hide();
688 stacked->blockSignals(true);
689 stacked->setCurrentIndex(index);
690 stacked->blockSignals(false);
691 m_pageComboBox->blockSignals(true);
692 m_pageComboBox->setCurrentIndex(index);
693 m_pageComboBox->blockSignals(false);
694 }
695
696 if (index < 0 || index >= curves.size()) { // Invalid index
697 QDEBUG << "Invalid index" << index;
698 return;
699 }
700 m_value = index;
701 switch(graphtypes[index]) {
702 case GraphType::GRAPH_FTABLE: {
703 int ftable = getTableNumForIndex(index);
704 if (m_value2 != ftable) {
705 m_value2 = ftable;
706 m_value2Changed = true;
707 }
708 if(m_drawTableInfo) {
709 auto curve = curves[index];
710 auto text = QString("%1 pts (%2, %3)")
711 .arg(curve->get_size())
712 .arg(curve->get_max(), 0, 'f', 3)
713 .arg(curve->get_min(), 0, 'f', 3);
714 m_label->setText(text);
715 m_label->show();
716 }
717 if(origRequest != -2) {
718 drawGraph(curves[index], index);
719 }
720 break;
721 }
722 case GraphType::GRAPH_SPECTRUM:
723 m_value2 = -1;
724 m_label->hide();
725 break;
726 case GraphType::GRAPH_AUDIOSIGNAL:
727 m_value2 = -1;
728 m_label->hide();
729 break;
730 }
731 scaleGraph(index);
732
733 }
734
indexChanged(int index)735 void QuteGraph::indexChanged(int index)
736 {
737 #ifdef USE_WIDGET_MUTEX
738 widgetLock.lockForRead();
739 #endif
740 if (m_channel == "") {
741 setInternalValue(index);
742 }
743 else {
744 QPair<QString, double> channelValue(m_channel, index);
745 emit newValue(channelValue);
746 }
747 if (m_channel2 == "") {
748 setValue2(getTableNumForIndex(index));
749 }
750 else {
751 QPair<QString, double> channel2Value(m_channel2, getTableNumForIndex(index));
752 emit newValue(channel2Value);
753 }
754
755 #ifdef USE_WIDGET_MUTEX
756 widgetLock.unlock();
757 #endif
758
759 }
760
clearCurves()761 void QuteGraph::clearCurves()
762 {
763 // curveLock.lock();
764 m_widget->blockSignals(true);
765 static_cast<StackedLayoutWidget *>(m_widget)->clearCurves();
766 m_widget->blockSignals(false);
767 m_pageComboBox->blockSignals(true);
768 m_pageComboBox->clear();
769 m_pageComboBox->blockSignals(false);
770 curves.clear();
771 graphtypes.clear();
772 lines.clear();
773 polygons.clear();
774 m_gridlines.clear();
775 m_gridTextsX.clear();
776 m_gridTextsY.clear();
777 m_spectrumPeakTexts.clear();
778 m_spectrumPeakMarkers.clear();
779 m_showPeak = false;
780 m_showPeakTemp = false;
781 m_showPeakCenterFrequency = 1000;
782 // curveLock.unlock();
783 }
784
addCurve(Curve * curve)785 void QuteGraph::addCurve(Curve * curve)
786 {
787 QMutexLocker locker(&curveLock);
788 Q_ASSERT(curve != nullptr);
789 GraphType graphType;
790 QGraphicsView *view;
791 CurveType type = curve->get_type();
792 if(type == CURVE_SPECTRUM) {
793 if(!m_enableDisplays)
794 return;
795 graphType = GraphType::GRAPH_SPECTRUM;
796 auto spectralView = new SpectralView(m_widget);
797 view = static_cast<QGraphicsView*>(spectralView);
798 connect(spectralView, SIGNAL(mouseReleased()), this, SLOT(mouseReleased()));
799 view->setRenderHint(QPainter::Antialiasing);
800 } else if(type == CURVE_FTABLE) {
801 if(!m_enableTables)
802 return;
803 graphType = GraphType::GRAPH_FTABLE;
804 view = new QGraphicsView(m_widget);
805 view->setRenderHint(QPainter::Antialiasing);
806 } else {
807 if(!m_enableDisplays)
808 return;
809 graphType = GraphType::GRAPH_AUDIOSIGNAL;
810 view = new QGraphicsView(m_widget);
811 }
812 QGraphicsScene *scene = new QGraphicsScene(view);
813 view->setContextMenuPolicy(Qt::NoContextMenu);
814 view->setScene(scene);
815 view->setObjectName(curve->get_caption());
816 auto scrollbarPolicy = property("QCS_showScrollbars").toBool() ? Qt::ScrollBarAsNeeded
817 : Qt::ScrollBarAlwaysOff;
818 view->setHorizontalScrollBarPolicy(scrollbarPolicy);
819 view->show();
820 scene->setBackgroundBrush(QBrush(Qt::black));
821 lines.append(QVector<QGraphicsLineItem *>());
822 QVector<QGraphicsLineItem *> gridLinesVector;
823 QVector<QGraphicsTextItem *> gridTextVectorX;
824 QVector<QGraphicsTextItem *> gridTextVectorY;
825 qreal freqStep = 1000.0;
826 // Max. samplerate. We generate a grid for this and can make lines/labels visible or
827 // not depending on the actual sample rate
828 qreal sr = 96000;
829 int numTicksX = (int)(sr*0.5 / freqStep);
830 int numTicksY = m_numticksY;
831 auto gridpen = QPen(QColor(90, 90, 90));
832 auto gridpen2 = QPen(QColor(60, 60, 60));
833 gridpen.setCosmetic(true);
834 gridpen2.setCosmetic(true);
835
836 auto marker = new QGraphicsRectItem();
837 marker->setVisible(false);
838 auto markerpen = QPen(QColor(250, 250, 250));
839 markerpen.setCosmetic(true);
840 marker->setPen(markerpen);
841 marker->setBrush(QBrush(QColor(255, 255, 255, 100)));
842 m_spectrumPeakMarkers.append(marker);
843 scene->addItem(marker);
844 auto markerText = new QGraphicsTextItem();
845 markerText->setDefaultTextColor(markerpen.color());
846 markerText->setVisible(false);
847 markerText->setFlags(QGraphicsItem::ItemIgnoresTransformations);
848 markerText->setFont(QFont("Sans", 8));
849 scene->addItem(markerText);
850 m_spectrumPeakTexts.append(markerText);
851 m_dbRange = 100;
852
853 if(graphType == GraphType::GRAPH_SPECTRUM) {
854 for (int i = 0 ; i < numTicksY; i++) {
855 // 0 - 0db, 5 -> 100 dB (6:120
856 QGraphicsLineItem *gridLine = new QGraphicsLineItem();
857 gridLine->setPen(gridpen);
858 scene->addItem(gridLine);
859 gridLinesVector.append(gridLine);
860 QGraphicsTextItem *gridText = new QGraphicsTextItem();
861 gridText->setDefaultTextColor(Qt::gray);
862 gridText->setFlags(QGraphicsItem::ItemIgnoresTransformations);
863 int dbs = round(float(i)/numTicksY * 120.0);
864 gridText->setHtml(QString("<div style=\"background:#000000;\">-%1 </p>"
865 ).arg(dbs));
866 gridText->setFont(QFont("Sans", 6));
867 gridText->setVisible(false);
868 scene->addItem(gridText);
869 gridTextVectorY.append(gridText);
870
871 }
872
873 for (int i = 0; i < numTicksX; i++) {
874 QGraphicsLineItem *gridLine = new QGraphicsLineItem();
875 gridLine->setPen(i%2 == 0 ? gridpen : gridpen2);
876 scene->addItem(gridLine);
877 gridLinesVector.append(gridLine);
878 QGraphicsTextItem *gridText = new QGraphicsTextItem();
879 gridText->setDefaultTextColor(Qt::gray);
880 gridText->setFlags(QGraphicsItem::ItemIgnoresTransformations);
881 if (i > 0 && i%2==0) {
882 // double kHz = i*((numTicksX-1.0)/numTicksX) * 2.0;
883 double kHz = i * freqStep / 1000.0 ;
884 if(i%10==0)
885 gridText->setHtml(
886 QString("<div style=\"background:#000000;\">%1k</p>")
887 .arg(kHz, 2, 'f', 1));
888 else
889 gridText->setHtml(
890 QString("<div style=\"background:#000000;\">%1</p>")
891 .arg(kHz, 2, 'f', 1));
892
893 }
894 gridText->setFont(QFont("Sans", 6));
895 gridText->setVisible(false);
896 scene->addItem(gridText);
897 gridTextVectorX.append(gridText);
898 }
899 }
900
901 m_gridlines.append(gridLinesVector);
902 m_gridTextsX.append(gridTextVectorX);
903 m_gridTextsY.append(gridTextVectorY);
904
905 graphtypes.append(graphType);
906
907 QGraphicsPolygonItem * item = new QGraphicsPolygonItem();
908 auto graphPen = QPen(Qt::yellow);
909 graphPen.setCosmetic(true);
910 item->setPen(graphPen);
911 item->show();
912 polygons.append(item);
913 scene->addItem(item);
914 view->setResizeAnchor (QGraphicsView::NoAnchor);
915 // view->setFocusPolicy(Qt::NoFocus);
916 m_pageComboBox->blockSignals(true);
917 QString curveTitle = curve->get_caption().trimmed();
918 if(curveTitle.endsWith(":"))
919 curveTitle = curveTitle.mid(0, curveTitle.size()-1);
920 m_pageComboBox->addItem(curveTitle);
921 // m_pageComboBox->addItem(curve->get_title());
922 m_pageComboBox->blockSignals(false);
923 // curveLock.lock();
924 static_cast<StackedLayoutWidget *>(m_widget)->addWidget(view);
925 curves.append(curve);
926 if (m_value == curves.size() - 1) {
927 // If new curve created corresponds to current stored value
928 changeCurve(m_value);
929 }
930 }
931
getCurveIndex(Curve * curve)932 int QuteGraph::getCurveIndex(Curve * curve)
933 {
934 Q_ASSERT(curve != nullptr);
935 int index = -1;
936 for (int i = 0; i < curves.size(); i++) {
937 if (curves[i] == curve) {
938 index = i;
939 break;
940 }
941 }
942 return index;
943 }
944
getView(int index)945 QGraphicsView * QuteGraph::getView(int index) {
946 StackedLayoutWidget *widget_ = static_cast<StackedLayoutWidget *>(m_widget);
947 QGraphicsView *view = static_cast<QGraphicsView *>(widget_->widget(index));
948 return view;
949 }
950
drawGraph(Curve * curve,int index)951 void QuteGraph::drawGraph(Curve *curve, int index) {
952 // QString caption = curve->get_caption();
953 // auto view = getView(index);
954 // switch(graphtypes[index]) {
955 switch(curve->get_type()) {
956 // case GraphType::GRAPH_FTABLE:
957 case CurveType::CURVE_FTABLE:
958 // drawFtable(curve, index);
959 // view->setRenderHint(QPainter::Antialiasing);
960 drawFtablePath(curve, index);
961 scaleGraph(index);
962 break;
963 // case GraphType::GRAPH_SPECTRUM:
964 case CurveType::CURVE_SPECTRUM:
965 // view->setRenderHint(QPainter::Antialiasing);
966 drawSpectrum(curve, index);
967 // drawSpectrumPath(curve, index);
968 changeCurve(-2); //update curve
969 break;
970 case CurveType::CURVE_AUDIOSIGNAL:
971 // case GraphType::GRAPH_AUDIOSIGNAL:
972 // drawSignal(curve, index);
973 drawSignalPath(curve, index);
974 changeCurve(-2); //update curve
975 break;
976 }
977 }
978
setCurveData(Curve * curve)979 void QuteGraph::setCurveData(Curve * curve)
980 {
981 Q_ASSERT(curve != nullptr);
982 int index = getCurveIndex(curve);
983
984 if (index >= curves.size() ||
985 index < 0 ||
986 index != m_value) {
987 return;
988 }
989
990 /*
991 if(!m_enableTables && graphtypes[index] == GraphType::GRAPH_FTABLE)
992 return;
993 */
994
995 auto view = getView(index);
996
997 // Refitting curves in view resets the scrollbar so we need the previous value
998 int viewPosx = view->horizontalScrollBar()->value();
999 int viewPosy = view->verticalScrollBar()->value();
1000
1001 drawGraph(curve, index);
1002 view->horizontalScrollBar()->setValue(viewPosx);
1003 view->verticalScrollBar()->setValue(viewPosy);
1004 }
1005
applyInternalProperties()1006 void QuteGraph::applyInternalProperties()
1007 {
1008 QuteWidget::applyInternalProperties();
1009 if(property("QCS_showSelector").toBool()) {
1010 m_pageComboBox->show();
1011 } else {
1012 m_pageComboBox->hide();
1013 }
1014 changeCurve(-2); // Redraw
1015 m_drawGrid = property("QCS_showGrid").toBool();
1016 m_drawTableInfo = property("QCS_showTableInfo").toBool();
1017
1018 showScrollbars(property("QCS_showScrollbars").toBool());
1019 }
1020
drawFtablePath(Curve * curve,int index)1021 void QuteGraph::drawFtablePath(Curve *curve, int index) {
1022 Q_ASSERT(index >= 0);
1023 QGraphicsScene *scene = this->getView(index)->scene();
1024 // QGraphicsScene *scene = static_cast<QGraphicsView *>(static_cast<StackedLayoutWidget *>(m_widget)->widget(index))->scene();
1025
1026 double max = curve->get_max();
1027 max = max == 0 ? 1: max;
1028 int curveSize = curve->get_size();
1029
1030 int decimate = curveSize /1024;
1031 if (decimate == 0) {
1032 decimate = 1;
1033 }
1034 auto pen = QPen(QColor(255, 45, 7), 0);
1035
1036 auto rect = this->rect();
1037 int width = rect.width();
1038 int step = curveSize / width;
1039 if(step == 0)
1040 step = 1;
1041
1042 QPainterPath path;
1043 for (int i = 0; i < curveSize; i+=step) {
1044 double value = curve->get_data(i);
1045 path.lineTo(QPointF(i, -value));
1046 }
1047 scene->clear();
1048 if(step > 1) {
1049 pen.setWidth(0);
1050 }
1051 scene->addPath(path, pen);
1052 }
1053
1054
drawFtable(Curve * curve,int index)1055 void QuteGraph::drawFtable(Curve * curve, int index)
1056 {
1057 // bool live = curve->getOriginal() != 0;
1058 Q_ASSERT(index >= 0);
1059 QString caption = curve->get_caption();
1060 if (caption.isEmpty()) {
1061 return;
1062 }
1063 QGraphicsScene *scene = static_cast<QGraphicsView *>(static_cast<StackedLayoutWidget *>(m_widget)->widget(index))->scene();
1064 double max = curve->get_max();
1065 max = max == 0 ? 1: max;
1066 int size = (int) curve->get_size();
1067 int decimate = size /1024;
1068 if (decimate == 0) {
1069 decimate = 1;
1070 }
1071 auto pen = QPen(QColor(255, 45, 7));
1072 pen.setCosmetic(true);
1073 if (lines[index].size() != size) {
1074 foreach (QGraphicsLineItem *line, lines[index]) {
1075 scene->removeItem(line);
1076 delete line;
1077 }
1078 lines[index].clear();
1079 for (int i = 0; i < size; i++) {
1080 if (decimate == 0 || i%decimate == 0) {
1081 QGraphicsLineItem *line = new QGraphicsLineItem(i, 0, i, 0);
1082 line->setPen(pen);
1083 lines[index].append(line);
1084 scene->addItem(line);
1085 }
1086 }
1087 }
1088 for (int i = 0; i < lines[index].size(); i++) { //skip first item, which is base line
1089 QGraphicsLineItem *line = static_cast<QGraphicsLineItem *>(lines[index][i]);
1090 MYFLT value = curve->get_data((i * decimate));
1091 line->setLine((i * decimate), 0, (i * decimate), -value );
1092 line->show();
1093 }
1094 scaleGraph(index);
1095 }
1096
drawSpectrumPath(Curve * curve,int index)1097 void QuteGraph::drawSpectrumPath(Curve *curve, int index) {
1098 int curveSize = curve->get_size();
1099 QGraphicsScene *scene = static_cast<QGraphicsView *>(static_cast<StackedLayoutWidget *>(m_widget)->widget(index))->scene();
1100 QPainterPath path;
1101 double db0 = m_ud->zerodBFS;
1102 path.moveTo(0, -20.0*log10(fabs(curve->get_data(0))/db0));
1103 for(int i=1; i < curveSize; i++) {
1104 double value = 20.0*log10(fabs(curve->get_data(i))/db0);
1105 path.lineTo(QPointF(i, -value));
1106 }
1107 scene->clear();
1108 auto pen = QPen(Qt::yellow);
1109 pen.setCosmetic(true);
1110
1111 QPainterPath gridPath;
1112 QPainter painter;
1113
1114 if(m_drawGrid) {
1115 int sr = (int)this->getSr();
1116 int nyquist = sr / 2;
1117 int step = 1000;
1118 int numTicksX = nyquist / step;
1119 int numTicksY = 7;
1120 printf("grid: nyquist: %d, numticks: %d\n", nyquist, numTicksX);
1121
1122 auto gridPen = QPen(QColor(40, 40, 40));
1123 gridPen.setCosmetic(true);
1124 auto textColor = QColor(128, 128, 128);
1125 qreal curveSizeF = (qreal)curveSize;
1126 auto font = QFont("Sans");
1127 font.setPixelSize(9);
1128 const int maxy = 110;
1129 for (int i = 1; i < numTicksX; i++) {
1130 qreal freq = i * step;
1131 qreal x = freq/nyquist * curveSizeF;
1132 gridPath.moveTo(x, 0);
1133 gridPath.lineTo(x, maxy);
1134 if(i%2 == 0) {
1135 auto item = scene->addText(QString::number(freq/1000.0, 'f', 1), font);
1136 item->setDefaultTextColor(textColor);
1137 item->setPos(x, 0);
1138 item->setFlag(item->ItemIgnoresTransformations, true);
1139 }
1140 }
1141 for (int i=1; i < numTicksY-1; i++) {
1142 qreal y = (qreal)i/numTicksY * maxy;
1143 gridPath.moveTo(0, y);
1144 gridPath.lineTo(curveSizeF, y);
1145 }
1146 scene->addPath(gridPath, gridPen);
1147 }
1148 scene->addPath(path, pen);
1149 }
1150
spectrumGetPeak(Curve * curve,double freq,double bandwidth)1151 size_t QuteGraph::spectrumGetPeak(Curve *curve, double freq, double bandwidth) {
1152 qreal sr = this->getSr(44100.);
1153 qreal nyquist = sr * 0.5;
1154 size_t curveSize = curve->get_size();
1155 qreal minfreq = freq - bandwidth*0.5;
1156 qreal maxfreq = freq + bandwidth*0.5;
1157 minfreq = qMax(1.0, minfreq);
1158 maxfreq = qMax(minfreq, maxfreq);
1159 size_t index0 = (size_t)(minfreq / nyquist * curveSize);
1160 index0 = index0 < curveSize - 1 ? index0 : curveSize - 1;
1161 size_t index1 = (size_t)(maxfreq / nyquist * curveSize);
1162 index1 = index1 < curveSize - 1 ? index1 : curveSize - 1;
1163 qreal maxvalue = 0;
1164 size_t maxindex = 0;
1165 if(!m_frozen) {
1166 for(int i=index0; i<index1; i++) {
1167 auto data = curve->get_data(i);
1168 if (data > maxvalue) {
1169 maxvalue = data;
1170 maxindex = i;
1171 }
1172 }
1173 }
1174 else {
1175 for(int i=index0; i<index1; i++) {
1176 auto data = frozenCurve[i];
1177 if (data > maxvalue) {
1178 maxvalue = data;
1179 maxindex = i;
1180 }
1181 }
1182 }
1183 if(maxindex > curveSize - 1 || maxindex < 0) {
1184 qDebug() << "spectrum peak: wrong index " << maxindex;
1185 return 0;
1186 }
1187 return maxindex;
1188 }
1189
mton(double midinote)1190 QString mton(double midinote) {
1191 // C C# D D# E F F# G G# A Bb B
1192 static const int _pc2idx[] = {2, 2, 3, 3, 4, 5, 5, 6, 6, 0, 1, 1};
1193 static const int _pc2alt[] = {0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 2, 0};
1194 static const char _alts[] = " #b";
1195
1196 QString dst = "";
1197 double m = midinote;
1198
1199 int octave = (int) (m / 12 - 1);
1200 int pc = (int)m % 12;
1201 int cents = round((m - floor(m)) * 100.0);
1202 int sign;
1203
1204 if (cents == 0) {
1205 sign = 0;
1206 } else if (cents <= 50) {
1207 sign = 1;
1208 } else {
1209 cents = 100 - cents;
1210 sign = -1;
1211 pc += 1;
1212 if (pc == 12) {
1213 pc = 0;
1214 octave += 1;
1215 }
1216 }
1217 if(octave >= 0) {
1218 dst.append('0' + octave);
1219 } else {
1220 dst.append('-');
1221 dst.append('0' - octave);
1222 }
1223 dst.append('A' + _pc2idx[pc]);
1224 int32_t alt = _pc2alt[pc];
1225 if(alt > 0) {
1226 dst.append(_alts[alt]);
1227 }
1228 if(sign == 1) {
1229 dst.append('+');
1230 if(cents != 50) {
1231 dst += QString::number(cents);
1232 }
1233 } else if(sign == -1) {
1234 dst.append('-');
1235 if(cents != 50) {
1236 dst += QString::number(cents);
1237 }
1238 }
1239 return dst;
1240 }
1241
1242
quantizePoint(int x,int y,int resx,int resy)1243 QPoint quantizePoint(int x, int y, int resx, int resy) {
1244 x = static_cast<int>(round((double)x / resx) * resx);
1245 y = static_cast<int>(round((double)y / resy) * resy);
1246 return QPoint(x, y);
1247 }
1248
freezeSpectrum(bool status)1249 void QuteGraph::freezeSpectrum(bool status) {
1250 int index = m_value;
1251 auto curve = curves[index];
1252 if(status) {
1253 m_frozen = true;
1254 frozenCurve.resize(curve->get_size());
1255 for(int i=0; i < curve->get_size(); i++) {
1256 frozenCurve[i] = curve->get_data(i);
1257 }
1258 } else {
1259 m_frozen = false;
1260 }
1261
1262 }
1263
drawSpectrum(Curve * curve,int index)1264 void QuteGraph::drawSpectrum(Curve *curve, int index) {
1265 if(!curveLock.tryLock())
1266 return;
1267 int curveSize = curve->get_size();
1268 if(curveSize != frozenCurve.size() && graphtypes[index] == GraphType::GRAPH_SPECTRUM)
1269 freezeSpectrum(false);
1270 auto view = getView(index);
1271 // QGraphicsScene *scene = static_cast<QGraphicsView *>(static_cast<StackedLayoutWidget *>(m_widget)->widget(index))->scene();
1272 QVector<QPointF> polygonPoints;
1273 polygonPoints.resize(curveSize + 2);
1274 double dbRange = m_dbRange;
1275 polygonPoints[0] = QPointF(0, dbRange);
1276 double db0 = m_ud->zerodBFS;
1277
1278 if(!m_frozen) {
1279 for (int i = 0; i < (int) curveSize; i++) {
1280 auto data = curve->get_data(i);
1281 double db = data > 0.000001 ? 20.0*log10(data/db0) : -dbRange;
1282 // double value = 20.0*log10(fabs(curve->get_data(i))/db0);
1283 polygonPoints[i+1] = QPointF(i, -db); //skip first item, which is base line
1284 }
1285 } else {
1286 for (int i = 0; i < frozenCurve.size(); i++) {
1287 auto data = frozenCurve[i];
1288 double db = data > 0.000001 ? 20.0*log10(data/db0) : -dbRange;
1289 // double value = 20.0*log10(fabs(curve->get_data(i))/db0);
1290 polygonPoints[i+1] = QPointF(i, -db); //skip first item, which is base line
1291 }
1292 }
1293
1294 polygonPoints.back() = QPointF(curveSize - 1, dbRange);
1295 polygons[index]->setPolygon(QPolygonF(polygonPoints));
1296
1297 // m_pageComboBox->setItemText(index, curve->get_caption());
1298 // draw Grid
1299 int numTicksY = m_numticksY;
1300 auto gridlinesvec = m_gridlines[index];
1301 auto gridtextvecx = m_gridTextsX[index];
1302 auto gridtextvecy = m_gridTextsY[index];
1303 qreal sr = this->getSr(44100.0);
1304 qreal nyquist = sr * 0.5;
1305 qreal freqStep = 1000.0; // TODO: allow to configure this
1306 int numTicksX = (int)(nyquist / freqStep);
1307 // TODO: fix y axis
1308 if(m_drawGrid) {
1309 gridtextvecy[0]->setVisible(true);
1310 for (int i = 1; i < numTicksY; i++) {
1311 int y = float(i)/(numTicksY) * dbRange;
1312 gridlinesvec[i]->setLine(0, y, curveSize, y);
1313 gridlinesvec[i]->setVisible(true);
1314 gridtextvecy[i]->setPos(0, y);
1315 gridtextvecy[i]->setVisible(true);
1316 }
1317
1318 for (int i = 1; i < numTicksX; i++) {
1319 // qreal x = i * qreal(curveSize)/numTicksX;
1320 qreal freq = i * freqStep;
1321 qreal x = freq/nyquist * curveSize;
1322 int idx = i + m_numticksY;
1323 gridlinesvec[idx]->setLine(x, 0, x, dbRange);
1324 gridlinesvec[idx]->setVisible(true);
1325 gridtextvecx[i]->setPos(x, 0);
1326 gridtextvecx[i]->setVisible(true);
1327 }
1328 } else {
1329 for(int i=0; i < numTicksY; i++) {
1330 gridlinesvec[i]->setVisible(false);
1331 gridtextvecy[i]->setVisible(false);
1332 }
1333 for (int i = 0; i < numTicksX; i++) {
1334 gridlinesvec[i+m_numticksY]->setVisible(false);
1335 gridtextvecx[i]->setVisible(false);
1336 }
1337 }
1338
1339 if(m_showPeak || m_showPeakTemp) {
1340 auto freq = m_showPeakTemp ? m_showPeakTempFrequency : m_showPeakCenterFrequency;
1341 double bandwidth;
1342 if(m_showPeakTemp) {
1343 // if using the mouth to point at a near peak, we take zoom into account and
1344 // the bandwidth is a fraction of the displayed frequency range.
1345 auto zoomx = property("QCS_zoomx").toDouble();
1346 bandwidth = nyquist / zoomx / 8.0;
1347 }
1348 else
1349 bandwidth = freq * m_showPeakRelativeBandwidth;
1350 size_t peakIndex = this->spectrumGetPeak(curve, freq, bandwidth);
1351 auto data = m_frozen ? frozenCurve[peakIndex] : curve->get_data(peakIndex);
1352 double db = data > 0.000001 ? 20.0*log10(data/db0) : -dbRange;
1353 auto marker = m_spectrumPeakMarkers[index];
1354 auto view = this->getView(index);
1355 auto vcenter = view->mapFromScene(QPointF(peakIndex, -db));
1356 auto markerLeftTop = view->mapToScene(QPoint(vcenter.x() - 4, vcenter.y()-4));
1357 auto markerRightBottom = view->mapToScene(vcenter.x() + 4, vcenter.y()+4);
1358 marker->setRect(markerLeftTop.x(), markerLeftTop.y(),
1359 markerRightBottom.x()-markerLeftTop.x(),
1360 markerRightBottom.y() - markerLeftTop.y());
1361 auto markerText = m_spectrumPeakTexts[index];
1362 auto textPos = view->mapToScene(vcenter.x() + 10, vcenter.y() - 12);
1363 auto markerY = m_lastTextMarkerY <= 0 ? textPos.y() :
1364 textPos.y() * 0.2 + m_lastTextMarkerY * 0.8;
1365 auto markerX = m_lastTextMarkerX <= 0 ? textPos.x() :
1366 textPos.x() * 0.3 + m_lastTextMarkerX * 0.7;
1367 m_lastTextMarkerY = markerY;
1368 m_lastTextMarkerX = markerX;
1369 markerText->setPos(markerX, markerY);
1370 double factor = nyquist / curveSize;
1371 double peakFreq;
1372 if(peakIndex <= 1 || peakIndex >= curveSize - 2) {
1373 peakFreq = factor * peakIndex;
1374 }
1375 else {
1376 double freq0 = qreal(peakIndex-1)*factor;
1377 double freq1 = qreal(peakIndex)*factor;
1378 double freq2 = qreal(peakIndex+1)*factor;
1379 double amp1 = data;
1380 double amp0, amp2;
1381 if(!m_frozen) {
1382 amp0 = curve->get_data(peakIndex-1);
1383 amp2 = curve->get_data(peakIndex+1);
1384 } else {
1385 amp0 = frozenCurve[peakIndex-1];
1386 amp2 = frozenCurve[peakIndex+1];
1387 }
1388 amp1 *= amp1;
1389 amp0 *= amp0;
1390 amp2 *= amp2;
1391 peakFreq = (freq0*amp0 + freq1*amp1 + freq2*amp2) / (amp0+amp1+amp2);
1392 }
1393 peakFreq = m_lastPeakFreq <= 0 ? peakFreq : peakFreq * 0.2 + m_lastPeakFreq * 0.8;
1394 m_lastPeakFreq = peakFreq;
1395 double a4 = csoundGetA4(this->m_ud->csound);
1396 double midinote;
1397 QString notename;
1398 if(peakFreq > 10) {
1399 midinote = 12.0 * log2(peakFreq / a4) + 69.0;
1400 notename = mton(midinote);
1401 }
1402 else {
1403 midinote = 0;
1404 notename = "LOW";
1405 }
1406 markerText->setPlainText(QString("%1 Hz (%2)").arg((int)(peakFreq+0.5)).arg(notename));
1407
1408 marker->setVisible(true);
1409 markerText->setVisible(true);
1410 if(m_peakChannelPtr != nullptr) {
1411 *m_peakChannelPtr = peakFreq;
1412 }
1413 }
1414 else {
1415 if(m_getPeakChannel != nullptr) {
1416 *m_peakChannelPtr = 0;
1417 }
1418 }
1419 curveLock.unlock();
1420 }
1421
drawSignalPath(Curve * curve,int index)1422 void QuteGraph::drawSignalPath(Curve *curve, int index) {
1423 int curveSize = curve->get_size();
1424 QPainterPath path;
1425 auto zerodbfs = m_ud->zerodBFS;
1426 for(int i=0; i<curveSize; i++) {
1427 auto value = curve->get_data(i)/zerodbfs;
1428 path.lineTo(i, value);
1429 }
1430 QPainterPath grid;
1431 grid.moveTo(0, 0);
1432 grid.lineTo(curveSize, 0);
1433
1434 QGraphicsScene *scene = static_cast<QGraphicsView *>(static_cast<StackedLayoutWidget *>(m_widget)->widget(index))->scene();
1435 auto pen = QPen(QColor(255, 193, 7), 0);
1436 scene->clear();
1437 scene->addPath(grid, QPen(QColor(40, 40, 40), 0));
1438 scene->addPath(path, pen);
1439 }
1440
drawSignal(Curve * curve,int index)1441 void QuteGraph::drawSignal(Curve *curve, int index)
1442 {
1443 int curveSize = curve->get_size();
1444 QVector<QPointF> polygonPoints;
1445 polygonPoints.resize(curveSize + 2);
1446 polygonPoints[0] = QPointF(0,0);
1447 for (int i = 0; i < (int) curveSize; i++) {
1448 double value = curve->get_data(i)/m_ud->zerodBFS;
1449 polygonPoints[i + 1] = QPointF(i, value); //skip first item, which is base line
1450 }
1451 polygonPoints.back() = QPointF(curveSize - 1,0);
1452 polygons[index]->setPolygon(QPolygonF(polygonPoints));
1453 auto pen = QPen(QColor(255, 193, 7));
1454 pen.setCosmetic(true);
1455 polygons[index]->setPen(pen);
1456 polygons[index]->setBrush(Qt::NoBrush);
1457 m_pageComboBox->setItemText(index, curve->get_caption());
1458 }
1459
scaleGraph(int index)1460 void QuteGraph::scaleGraph(int index)
1461 {
1462 auto curve = curves[index];
1463
1464 double max = curves[index]->get_max();
1465 double min = curves[index]->get_min();
1466 double zoomx = property("QCS_zoomx").toDouble();
1467 double zoomy = property("QCS_zoomy").toDouble();
1468 // double span = max - min;
1469 // FIXME implement dispx, dispy and modex, modey
1470 int size = curve->get_size();
1471 double sizef = (double)size;
1472 auto view = getView(index);
1473 // view->setResizeAnchor(QGraphicsView::NoAnchor);
1474 auto graphType = graphtypes[index];
1475 if(graphType == GraphType::GRAPH_FTABLE && max != min) {
1476 double yrange = max - min;
1477 double factor = 1.17;
1478 view->setSceneRect(0, -max*factor - 0.05, sizef, yrange*factor);
1479 view->fitInView(0, -max*factor/zoomy, sizef/zoomx, yrange*factor/zoomy);
1480 } else if(graphType == GraphType::GRAPH_SPECTRUM) {
1481 double dbRange = m_dbRange;
1482 view->setSceneRect (0, -3, size, dbRange);
1483 view->fitInView(0, -3/zoomy, sizef/zoomx, dbRange/zoomy);
1484 } else { //from display opcode
1485 view->setSceneRect (0, -1, size, 2);
1486 // view->fitInView(0, -10./zoomy, (double) size/zoomx, 10./zoomy);
1487 view->fitInView(0, -2./zoomy, sizef/zoomx, 2./zoomy);
1488 }
1489
1490 }
1491
getTableNumForIndex(int index)1492 int QuteGraph::getTableNumForIndex(int index) {
1493 if (index < 0 || index >= curves.size() || curves.size() <= 0) {
1494 // Invalid index
1495 return -1;
1496 }
1497 int ftable = -1;
1498 if(graphtypes[index] == GraphType::GRAPH_FTABLE) {
1499 QString caption = curves[index]->get_caption();
1500 ftable= caption.mid(caption.indexOf(" ") + 1,
1501 caption.indexOf(":") - caption.indexOf(" ") - 1).toInt();
1502 }
1503 return ftable;
1504 }
1505
getIndexForTableNum(int ftable)1506 int QuteGraph::getIndexForTableNum(int ftable)
1507 {
1508 int index = -1;
1509 for (int i = 0; i < curves.size(); i++) {
1510 QString text = curves[i]->get_caption();
1511 if (text.contains("ftable")) {
1512 QStringList parts = text.split(QRegExp("[ :]"), QString::SkipEmptyParts);
1513 if (parts.size() > 1) {
1514 int num = parts.last().toInt();
1515 if (ftable == num) {
1516 index = i;
1517 break;
1518 }
1519 }
1520 }
1521 }
1522 return index;
1523 }
1524
setInternalValue(double value)1525 void QuteGraph::setInternalValue(double value)
1526 {
1527 m_value = value;
1528 m_valueChanged = true;
1529 }
1530
showScrollbars(bool show)1531 void QuteGraph::showScrollbars(bool show) {
1532 auto policy = show ? Qt::ScrollBarAsNeeded : Qt::ScrollBarAlwaysOff;
1533 for(int i=0; i < this->curves.size(); i++) {
1534 auto view = getView(i);
1535 view->setHorizontalScrollBarPolicy(policy);
1536 }
1537 m_showScrollbars = show;
1538 }
1539
1540 // ----------------------
~QuteTableWidget()1541 QuteTableWidget::~QuteTableWidget() {
1542 };
1543
reset()1544 void QuteTableWidget::reset() {
1545 QMutexLocker locker(&mutex);
1546 m_tabnum = 0;
1547 m_running = false;
1548 m_data = nullptr;
1549 m_tabsize = 0;
1550 if(m_autorange)
1551 m_maxy = 1.0;
1552 if(m_path != nullptr) {
1553 delete m_path;
1554 m_path = nullptr;
1555 }
1556 }
1557
paintGrid(QPainter * painter)1558 void QuteTableWidget::paintGrid(QPainter *painter) {
1559 int margin = m_margin;
1560 QString maxystr;
1561 if(m_maxy >= 10)
1562 maxystr = QString::number((int)m_maxy);
1563 else if(m_maxy >= 1)
1564 maxystr = QString::number(m_maxy, 'f', 1);
1565 else
1566 maxystr = QString::number(m_maxy, 'f', 2);
1567 auto rect = this->rect();
1568 const int yoffset = 0;
1569 auto x0 = rect.x() + margin;
1570 auto x1 = rect.x() + rect.width() - margin + 1;
1571 auto y0 = rect.y() + margin + yoffset;
1572 auto y1 = rect.y() + rect.height() - margin + yoffset ;
1573 auto ycenter = (y0+y1) / 2;
1574
1575 auto font = QFont({"Sans", 8});
1576 QFontMetrics fm(font);
1577 auto tabsizestr = QString::number(m_tabsize);
1578 const int textMargin = 4;
1579 painter->setBrush(Qt::NoBrush);
1580 painter->setPen(QPen(QColor(96, 96, 96), 0));
1581 painter->drawLine(rect.x() + fm.boundingRect(maxystr).width()+textMargin*2, y0, x1, y0);
1582 painter->drawLine(x0, ycenter, x1, ycenter);
1583 painter->drawLine(x0, y1, x1 - fm.boundingRect(tabsizestr).width() - 2, y1);
1584 painter->setPen(QColor(48, 48, 48));
1585 painter->drawLine(x0, (y0+ycenter)/2, x1, (y0+ycenter)/2);
1586 painter->drawLine(x0, (y1+ycenter)/2, x1, (y1+ycenter)/2);
1587
1588 painter->setPen(QColor(200, 200, 200));
1589 painter->setFont(font);
1590 painter->drawText(rect.x()+textMargin, y0+textMargin, maxystr);
1591 // right padding
1592 rect.setWidth(rect.width() - textMargin);
1593 painter->drawText(rect, Qt::AlignRight|Qt::AlignBottom, tabsizestr);
1594
1595 }
1596
paintEvent(QPaintEvent * event)1597 void QuteTableWidget::paintEvent(QPaintEvent *event) {
1598 QPainter painter(this);
1599 painter.setRenderHint(QPainter::Antialiasing);
1600 painter.setPen(Qt::NoPen);
1601 bool running = csoundEngineStatus(m_ud) == CsoundEngineStatus::Running;
1602 if(!running) {
1603 painter.setBrush(QColor(176, 0, 32));
1604 painter.drawRect(this->rect());
1605 painter.setPen(Qt::white);
1606 painter.drawText(this->rect(), Qt::AlignCenter, "Stopped");
1607 return;
1608 }
1609 if(m_tabnum <= 0) {
1610 // table not set
1611 painter.setBrush(QColor(0, 176, 32));
1612 painter.drawRect(this->rect());
1613 painter.setPen(Qt::white);
1614 painter.drawText(this->rect(), Qt::AlignCenter, "Table not set");
1615 return;
1616 }
1617 // running OK
1618 painter.setBrush(QColor(24, 24, 24));
1619 painter.drawRect(this->rect());
1620 painter.setBrush(Qt::NoBrush);
1621 if(m_path == nullptr)
1622 return;
1623 mutex.lock();
1624 if(m_showGrid) {
1625 this->paintGrid(&painter);
1626 }
1627 painter.setPen(QPen(m_color, 0));
1628 painter.drawPath(*m_path);
1629
1630 mutex.unlock();
1631 }
1632
setRange(double maxy)1633 void QuteTableWidget::setRange(double maxy) {
1634 if(maxy == 0.0) {
1635 m_autorange = true;
1636 } else {
1637 m_autorange = false;
1638 m_maxy = maxy;
1639 }
1640 }
1641
updatePath()1642 void QuteTableWidget::updatePath() {
1643 if(m_tabnum <= 0 && m_data == nullptr)
1644 return;
1645 if(csoundEngineStatus(m_ud) != CsoundEngineStatus::Running)
1646 return;
1647 int margin = m_margin;
1648 auto rect = this->rect();
1649 auto width = rect.width() - margin*2;
1650 auto height = rect.height() - margin*2;
1651 double maxy = this->m_maxy;
1652 double newmaxy = maxy;
1653 double newminy = -maxy;
1654 double xscale = width / (double)m_tabsize;
1655 double yscale = height * 0.5 / maxy;
1656 double y0 = rect.y() + margin;
1657 double x0 = rect.x() + margin;
1658 int step = m_tabsize / width;
1659 if(step == 0)
1660 step = 1;
1661
1662 auto path = new QPainterPath();
1663 double ydata = m_data[0];
1664 path->moveTo(x0, (ydata+maxy)*yscale+y0);
1665 for(int i=0; i < m_tabsize; i+=step) {
1666 ydata = -m_data[i];
1667 double x2 = i*xscale + x0;
1668 double y2 = (ydata+maxy)*yscale + y0;
1669 path->lineTo(x2, y2);
1670 if(ydata > newmaxy)
1671 newmaxy = ydata;
1672 else if (ydata < newminy)
1673 newminy = ydata;
1674 }
1675 mutex.lock();
1676 if(m_autorange) {
1677 m_maxy = newmaxy > -newminy ? ceil(newmaxy) : floor(-newminy);
1678 }
1679 if(m_path != nullptr)
1680 delete m_path;
1681 m_path = path;
1682 mutex.unlock();
1683 }
1684
updateData(int tabnum,bool check)1685 void QuteTableWidget::updateData(int tabnum, bool check) {
1686 if(check && csoundEngineStatus(m_ud) != CsoundEngineStatus::Running) {
1687 m_tabnum = 0;
1688 return;
1689 }
1690 if(tabnum == -1 || tabnum == m_tabnum ) {
1691 if(m_tabnum == 0 || m_data == nullptr || m_tabsize == 0) {
1692 qDebug() << "Table not set, can't update";
1693 return;
1694 }
1695 this->updatePath();
1696 this->update();
1697 return;
1698 }
1699 // Asked to change table ( or set for the first time )
1700 MYFLT *data;
1701 int tabsize = csoundGetTable(m_ud->csound, &data, tabnum);
1702 if(tabsize == 0 || data == nullptr) {
1703 qDebug() << "Table" << tabnum << "not found";
1704 this->reset();
1705 return;
1706 }
1707 mutex.lock();
1708 m_data = data;
1709 m_tabsize = tabsize;
1710 if(m_autorange && m_tabnum != tabnum) {
1711 m_maxy = 1.0;
1712 }
1713 m_tabnum = tabnum;
1714 mutex.unlock();
1715 this->updatePath();
1716 this->update();
1717 }
1718
1719
1720 // -------------------------
1721
~QuteTable()1722 QuteTable::~QuteTable() {};
1723
1724 // void QuteTable::mousePressEvent(QMouseEvent *event) {};
1725 // void QuteTable::mouseReleaseEvent(QMouseEvent *event) {};
1726
QuteTable(QWidget * parent)1727 QuteTable::QuteTable(QWidget *parent) : QuteWidget(parent) {
1728 m_widget = new QuteTableWidget(this);
1729 m_value = 0;
1730 m_tabnum = 0;
1731 // auto w = static_cast<QuteTableWidget*>(m_widget);
1732 setProperty("QCS_randomizable", false);
1733 m_widget->setContextMenuPolicy(Qt::NoContextMenu);
1734 m_widget->setMouseTracking(true);
1735 setProperty("QCS_range", 0.0);
1736 setColor(QColor(255, 193, 3));
1737 setProperty("QCS_showGrid", true);
1738 }
1739
setColor(QColor color)1740 void QuteTable::setColor(QColor color) {
1741 setProperty("QCS_color", color);
1742 static_cast<QuteTableWidget*>(m_widget)->setColor(color);
1743 }
1744
applyInternalProperties()1745 void QuteTable::applyInternalProperties() {
1746 QuteWidget::applyInternalProperties();
1747 auto w = static_cast<QuteTableWidget*>(m_widget);
1748 auto color = property("QCS_color").value<QColor>();
1749 w->setColor(color);
1750 w->setRange(property("QCS_range").toDouble());
1751 w->showGrid(property("QCS_showGrid").toBool());
1752 }
1753
createPropertiesDialog()1754 void QuteTable::createPropertiesDialog() {
1755 QuteWidget::createPropertiesDialog();
1756 dialog->setWindowTitle("Table Plot");
1757 auto label = new QLabel(dialog);
1758 label->setText("Color");
1759 layout->addWidget(label, 4, 0, Qt::AlignRight|Qt::AlignVCenter);
1760
1761 colorButton = new SelectColorButton(dialog);
1762 colorButton->setColor(property("QCS_color").value<QColor>());
1763 layout->addWidget(colorButton, 4, 1, Qt::AlignLeft|Qt::AlignVCenter);
1764
1765 double range = property("QCS_range").toDouble();
1766 rangeCheckBox = new QCheckBox("Fixed Range", dialog);
1767 rangeCheckBox->setToolTip("If checked the range is fixed to the value set on the right."
1768 "If the table has values outside this range these will not be"
1769 "displayed");
1770 rangeCheckBox->setChecked(range > 0.0);
1771 layout->addWidget(rangeCheckBox, 5, 1, Qt::AlignRight|Qt::AlignVCenter);
1772
1773 rangeSpinBox = new QDoubleSpinBox(dialog);
1774 rangeSpinBox->setRange(0.1, 999999.0);
1775 rangeSpinBox->setSingleStep(0.1);
1776 rangeSpinBox->setToolTip("Sets the max. range to plot. Disable this to use autorange"
1777 "With a fixed range, values outside the range will not be "
1778 "visiable");
1779 rangeSpinBox->setValue(range > 0 ? range : 1.0);
1780 layout->addWidget(rangeSpinBox, 5, 2, Qt::AlignLeft|Qt::AlignVCenter);
1781 connect(rangeCheckBox, SIGNAL(toggled(bool)), rangeSpinBox, SLOT(setEnabled(bool)));
1782
1783 gridCheckBox = new QCheckBox("Show Grid", dialog);
1784 gridCheckBox->setChecked(property("QCS_showGrid").toBool());
1785 layout->addWidget(gridCheckBox, 6, 1, Qt::AlignLeft|Qt::AlignVCenter);
1786
1787 }
1788
applyProperties()1789 void QuteTable::applyProperties() {
1790 #ifdef USE_WIDGET_MUTEX
1791 widgetLock.lockForWrite();
1792 #endif
1793 setProperty("QCS_color", colorButton->getColor());
1794 bool fixedrange = rangeCheckBox->isChecked();
1795 double range = rangeSpinBox->value();
1796 if(fixedrange) {
1797 Q_ASSERT(range > 0.0);
1798 setProperty("QCS_range", range);
1799 } else {
1800 setProperty("QCS_range", 0.0);
1801 }
1802 setProperty("QCS_showGrid", gridCheckBox->isChecked());
1803
1804 #ifdef USE_WIDGET_MUTEX
1805 widgetLock.unlock();
1806 #endif
1807 QuteWidget::applyProperties();
1808 }
1809
refreshWidget()1810 void QuteTable::refreshWidget() {
1811 Q_ASSERT(m_valueChanged);
1812 QMutexLocker locker(&mutex);
1813 m_valueChanged = false;
1814 if(csoundEngineStatus(m_csoundUserData) != CsoundEngineStatus::Running)
1815 return;
1816 auto w = static_cast<QuteTableWidget*>(m_widget);
1817 int tabnum = (int)m_value;
1818 w->blockSignals(true);
1819 w->updateData(tabnum, true);
1820 w->blockSignals(false);
1821 }
1822
getWidgetXmlText()1823 QString QuteTable::getWidgetXmlText() {
1824 xmlText = "";
1825 QXmlStreamWriter s(&xmlText);
1826 createXmlWriter(s); // --------- start
1827
1828 QColor color = property("QCS_color").value<QColor>();
1829 s.writeStartElement("color");
1830 s.writeTextElement("r", QString::number(color.red()));
1831 s.writeTextElement("g", QString::number(color.green()));
1832 s.writeTextElement("b", QString::number(color.blue()));
1833 s.writeEndElement();
1834
1835 auto range = property("QCS_range").toDouble();
1836 s.writeTextElement("range", QString::number(range, 'f', 2));
1837
1838 s.writeEndElement(); // --------- end
1839 return xmlText;
1840 }
1841
onStop()1842 void QuteTable::onStop() {
1843 mutex.lock();
1844 this->blockSignals(true);
1845 m_tabnum = 0;
1846 m_value = 0;
1847 m_valueChanged = true;
1848
1849 auto w = static_cast<QuteTableWidget*>(m_widget);
1850 w->setUserData(m_csoundUserData);
1851 w->blockSignals(true);
1852 w->reset();
1853 w->blockSignals(false);
1854 // this->applyInternalProperties();
1855 this->blockSignals(false);
1856 mutex.unlock();
1857 }
1858
setCsoundUserData(CsoundUserData * ud)1859 void QuteTable::setCsoundUserData(CsoundUserData *ud) {
1860 if(ud == nullptr) {
1861 qDebug()<<"CsoundUserData is null";
1862 return;
1863 }
1864 QMutexLocker locker(&mutex);
1865 m_csoundUserData = ud;
1866 auto w = static_cast<QuteTableWidget*>(m_widget);
1867 if(w == nullptr) {
1868 qDebug() << "widget is null";
1869 return;
1870 }
1871 w->setUserData(ud);
1872 connect(ud->csEngine, SIGNAL(stopSignal()), this, SLOT(onStop()));
1873 }
1874
1875 /*
1876 void QuteTable::setWidgetGeometry(int x, int y, int width, int height) {
1877
1878 if(csoundEngineStatus(m_csoundUserData)==CsoundEngineStatus::Running) {
1879 return;
1880 }
1881 QuteWidget::setWidgetGeometry(x,y,width, height);
1882 }
1883 */
1884
setValue(double value)1885 void QuteTable::setValue(double value) {
1886 if(m_value == value) {
1887 return;
1888 }
1889
1890 #ifdef USE_WIDGET_MUTEX
1891 widgetLock.lockForWrite();
1892 #endif
1893 mutex.lock();
1894 if(value == -1 && m_tabnum > 0) {
1895 // update data, don't change table number
1896 m_valueChanged = false;
1897 auto w = static_cast<QuteTableWidget*>(m_widget);
1898 w->blockSignals(true);
1899 w->updateData(m_tabnum);
1900 w->blockSignals(false);
1901 mutex.unlock();
1902 return;
1903 }
1904 m_value = value;
1905 m_valueChanged = true;
1906 m_tabnum = (int)value;
1907 mutex.unlock();
1908 static_cast<QuteTableWidget*>(m_widget)->updateData(m_tabnum);
1909
1910 #ifdef USE_WIDGET_MUTEX
1911 widgetLock.unlock();
1912 #endif
1913
1914 };
1915
setValue(QString s)1916 void QuteTable::setValue(QString s) {
1917 if(s.isEmpty())
1918 return;
1919 auto parts = s.splitRef(' ', QString::SkipEmptyParts);
1920 if(parts.size() == 0)
1921 return;
1922 if(parts[0] == "@set") {
1923 if(parts.size() != 2) {
1924 qDebug() << "@set message expects a table number (example: @set 103)";
1925 return;
1926 }
1927 int tabnum = parts[1].toInt();
1928 setValue((double)tabnum);
1929 } else if (parts[0] == "@update") {
1930 setValue(-1);
1931 } else
1932 qDebug() << "Message not supported:" << s;
1933 }
1934
~SpectralView()1935 SpectralView::~SpectralView() {};
1936
mouseReleaseEvent(QMouseEvent * ev)1937 void SpectralView::mouseReleaseEvent(QMouseEvent *ev) {
1938 emit mouseReleased();
1939 QGraphicsView::mouseReleaseEvent(ev);
1940 }
1941