OSCHandler.cpp
Go to the documentation of this file.
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4  Sonic Visualiser
5  An audio file viewer and annotation editor.
6  Centre for Digital Music, Queen Mary, University of London.
7  This file copyright 2006-2007 Chris Cannam and QMUL.
8 
9  This program is free software; you can redistribute it and/or
10  modify it under the terms of the GNU General Public License as
11  published by the Free Software Foundation; either version 2 of the
12  License, or (at your option) any later version. See the file
13  COPYING included with this distribution for more information.
14 */
15 
16 #include "MainWindow.h"
17 #include "data/osc/OSCQueue.h"
18 
19 #include "layer/WaveformLayer.h"
20 #include "view/ViewManager.h"
21 #include "view/Pane.h"
22 #include "view/PaneStack.h"
23 #include "data/model/WaveFileModel.h"
24 #include "widgets/CommandHistory.h"
25 #include "audio/AudioCallbackPlaySource.h"
26 #include "framework/Document.h"
27 #include "data/fileio/WavFileWriter.h"
28 #include "transform/TransformFactory.h"
29 #include "widgets/LevelPanWidget.h"
30 #include "widgets/LevelPanToolButton.h"
31 #include "widgets/AudioDial.h"
32 
33 #include <bqaudioio/SystemPlaybackTarget.h>
34 
35 #include <QFileInfo>
36 #include <QTime>
37 #include <QElapsedTimer>
38 
39 #if (QT_VERSION >= 0x050600)
40 #define NOW (QTime::currentTime().toString(Qt::ISODateWithMs))
41 #else
42 #define NOW (QTime::currentTime().toString(Qt::ISODate))
43 #endif
44 
45 void
46 MainWindow::handleOSCMessage(const OSCMessage &message)
47 {
48  QElapsedTimer timer;
49  timer.start();
50 
51  SVDEBUG << "OSCHandler at " << NOW << ": handling message: "
52  << message.toString() << endl;
53 
54  if (message.getMethod() == "open") {
55 
56  if (message.getArgCount() == 1 &&
57  message.getArg(0).canConvert(QVariant::String)) {
58  QString path = message.getArg(0).toString();
59  if (open(path, ReplaceMainModel) == FileOpenSucceeded) {
60  SVDEBUG << "OSCHandler: Opened path \""
61  << path << "\"" << endl;
62  } else {
63  SVCERR << "OSCHandler: File open failed for path \""
64  << path << "\"" << endl;
65  }
67  // file has been completely decoded...
68  } else {
69  SVCERR << "OSCHandler: Usage: /open <filename>" << endl;
70  }
71 
72  } else if (message.getMethod() == "openadditional") {
73 
74  if (message.getArgCount() == 1 &&
75  message.getArg(0).canConvert(QVariant::String)) {
76  QString path = message.getArg(0).toString();
77  if (open(path, CreateAdditionalModel) == FileOpenSucceeded) {
78  SVDEBUG << "OSCHandler: Opened additional path \""
79  << path << "\"" << endl;
80  } else {
81  SVCERR << "OSCHandler: File open failed for path \""
82  << path << "\"" << endl;
83  }
84  } else {
85  SVCERR << "OSCHandler: Usage: /openadditional <filename>" << endl;
86  }
87 
88  } else if (message.getMethod() == "recent" ||
89  message.getMethod() == "last") {
90 
91  int n = 0;
92  if (message.getMethod() == "recent" &&
93  message.getArgCount() == 1 &&
94  message.getArg(0).canConvert(QVariant::Int)) {
95  n = message.getArg(0).toInt() - 1;
96  }
97  std::vector<QString> recent = m_recentFiles.getRecent();
98  if (n >= 0 && n < int(recent.size())) {
99  QString path = recent[n];
100  if (open(path, ReplaceMainModel) == FileOpenSucceeded) {
101  SVDEBUG << "OSCHandler: Opened recent path \""
102  << path << "\"" << endl;
103  } else {
104  SVCERR << "OSCHandler: File open failed for path \""
105  << path << "\"" << endl;
106  }
107  } else {
108  SVCERR << "OSCHandler: Usage: /recent <n>" << endl;
109  SVCERR << " or /last" << endl;
110  }
111 
112  } else if (message.getMethod() == "save") {
113 
114  QString path;
115  if (message.getArgCount() == 1 &&
116  message.getArg(0).canConvert(QVariant::String)) {
117  path = message.getArg(0).toString();
118  if (QFileInfo(path).exists()) {
119  SVCERR << "OSCHandler: Refusing to overwrite existing file in save" << endl;
120  } else if (saveSessionFile(path)) {
121  SVDEBUG << "OSCHandler: Saved session to path \""
122  << path << "\"" << endl;
123  } else {
124  SVCERR << "OSCHandler: Save failed to path \""
125  << path << "\"" << endl;
126  }
127  } else {
128  SVCERR << "OSCHandler: Usage: /save <filename>" << endl;
129  }
130 
131  } else if (message.getMethod() == "export") {
132 
133  QString path;
134  if (getMainModel()) {
135  if (message.getArgCount() == 1 &&
136  message.getArg(0).canConvert(QVariant::String)) {
137  path = message.getArg(0).toString();
138  if (QFileInfo(path).exists()) {
139  SVCERR << "OSCHandler: Refusing to overwrite existing file in export" << endl;
140  } else {
141  WavFileWriter writer(path,
142  getMainModel()->getSampleRate(),
143  getMainModel()->getChannelCount(),
144  WavFileWriter::WriteToTemporary);
145  MultiSelection ms = m_viewManager->getSelection();
146  if (writer.writeModel
147  (getMainModel().get(),
148  ms.getSelections().empty() ? nullptr : &ms)) {
149  SVDEBUG << "OSCHandler: Exported audio to path \""
150  << path << "\"" << endl;
151  } else {
152  SVCERR << "OSCHandler: Export failed to path \""
153  << path << "\"" << endl;
154  }
155  }
156  }
157  } else {
158  SVCERR << "OSCHandler: Usage: /export <filename>" << endl;
159  }
160 
161  } else if (message.getMethod() == "exportlayer") {
162 
163  QString path;
164  if (message.getArgCount() == 1 &&
165  message.getArg(0).canConvert(QVariant::String)) {
166  path = message.getArg(0).toString();
167  if (QFileInfo(path).exists()) {
168  SVCERR << "OSCHandler: Refusing to overwrite existing file in layer export" << endl;
169  } else {
170  Pane *currentPane = nullptr;
171  Layer *currentLayer = nullptr;
172  if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
173  if (currentPane) currentLayer = currentPane->getSelectedLayer();
174  MultiSelection ms = m_viewManager->getSelection();
175  if (currentLayer) {
176  QString error;
177  if (exportLayerTo
178  (currentLayer, currentPane,
179  ms.getSelections().empty() ? nullptr : &ms,
180  path, error)) {
181  SVDEBUG << "OSCHandler: Exported layer \""
182  << currentLayer->getLayerPresentationName()
183  << "\" to path \"" << path << "\"" << endl;
184  } else {
185  SVCERR << "OSCHandler: Export failed to path \""
186  << path << "\"" << endl;
187  }
188  } else {
189  SVCERR << "OSCHandler: No current layer to export" << endl;
190  }
191  }
192  } else {
193  SVCERR << "OSCHandler: Usage: /exportlayer <filename>" << endl;
194  }
195 
196  } else if (message.getMethod() == "exportimage") {
197 
198  QString path;
199  if (message.getArgCount() == 1 &&
200  message.getArg(0).canConvert(QVariant::String)) {
201  path = message.getArg(0).toString();
202  if (QFileInfo(path).exists()) {
203  SVCERR << "OSCHandler: Refusing to overwrite existing file in image export" << endl;
204  } else {
205  Pane *currentPane = nullptr;
206  if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
207  MultiSelection ms = m_viewManager->getSelection();
208  if (currentPane) {
209  QImage *image = nullptr;
210  auto sel = ms.getSelections();
211  if (!sel.empty()) {
212  sv_frame_t sf0 = sel.begin()->getStartFrame();
213  sv_frame_t sf1 = sel.rbegin()->getEndFrame();
214  image = currentPane->renderPartToNewImage(sf0, sf1);
215  } else {
216  image = currentPane->renderToNewImage();
217  }
218  if (!image) {
219  SVCERR << "OSCHandler: Failed to create image from pane"
220  << endl;
221  } else if (!image->save(path, "PNG")) {
222  SVCERR << "OSCHandler: Export failed to image file \""
223  << path << "\"" << endl;
224  } else {
225  SVDEBUG << "OSCHandler: Exported pane to image file \""
226  << path << "\"" << endl;
227  }
228  delete image;
229  } else {
230  SVCERR << "OSCHandler: No current pane to export" << endl;
231  }
232  }
233  } else {
234  SVCERR << "OSCHandler: Usage: /exportimage <filename>" << endl;
235  }
236 
237  } else if (message.getMethod() == "exportsvg") {
238 
239  QString path;
240  if (message.getArgCount() == 1 &&
241  message.getArg(0).canConvert(QVariant::String)) {
242  path = message.getArg(0).toString();
243  if (QFileInfo(path).exists()) {
244  SVCERR << "OSCHandler: Refusing to overwrite existing file in SVG export" << endl;
245  } else {
246  Pane *currentPane = nullptr;
247  if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
248  MultiSelection ms = m_viewManager->getSelection();
249  if (currentPane) {
250  bool result = false;
251  auto sel = ms.getSelections();
252  if (!sel.empty()) {
253  sv_frame_t sf0 = sel.begin()->getStartFrame();
254  sv_frame_t sf1 = sel.rbegin()->getEndFrame();
255  result = currentPane->renderPartToSvgFile(path, sf0, sf1);
256  } else {
257  result = currentPane->renderToSvgFile(path);
258  }
259  if (!result) {
260  SVCERR << "OSCHandler: Export failed to SVG file \""
261  << path << "\"" << endl;
262  } else {
263  SVDEBUG << "OSCHandler: Exported pane to SVG file \""
264  << path << "\"" << endl;
265  }
266  } else {
267  SVCERR << "OSCHandler: No current pane to export" << endl;
268  }
269  }
270  } else {
271  SVCERR << "OSCHandler: Usage: /exportsvg <filename>" << endl;
272  }
273 
274  } else if (message.getMethod() == "jump" ||
275  message.getMethod() == "play") {
276 
277  if (getMainModel()) {
278 
279  sv_frame_t frame = m_viewManager->getPlaybackFrame();
280  bool selection = false;
281  bool play = (message.getMethod() == "play");
282 
283  if (message.getArgCount() == 1) {
284 
285  if (message.getArg(0).canConvert(QVariant::String) &&
286  message.getArg(0).toString() == "selection") {
287 
288  selection = true;
289 
290  } else if (message.getArg(0).canConvert(QVariant::String) &&
291  message.getArg(0).toString() == "end") {
292 
293  frame = getMainModel()->getEndFrame();
294 
295  } else if (message.getArg(0).canConvert(QVariant::Double)) {
296 
297  double time = message.getArg(0).toDouble();
298  if (time < 0.0) time = 0.0;
299 
300  frame = lrint(time * getMainModel()->getSampleRate());
301  }
302  }
303 
304  if (frame > getMainModel()->getEndFrame()) {
305  frame = getMainModel()->getEndFrame();
306  }
307 
308  if (play) {
309  m_viewManager->setPlaySelectionMode(selection);
310  }
311 
312  if (selection) {
313  MultiSelection::SelectionList sl = m_viewManager->getSelections();
314  if (!sl.empty()) {
315  frame = sl.begin()->getStartFrame();
316  }
317  }
318 
319  SVDEBUG << "OSCHandler: Setting playback frame to " << frame << endl;
320 
321  m_viewManager->setPlaybackFrame(frame);
322 
323  if (play) {
324  if (!m_playSource->isPlaying()) {
325  SVDEBUG << "OSCHandler: Play source is not yet playing, calling play()" << endl;
326  // handles audio device suspend/resume etc, as
327  // well as calling m_playSource->play(frame)
328  MainWindow::play();
329  } else {
330  SVDEBUG << "OSCHandler: Play source is already playing, not starting it" << endl;
331  }
332  } else {
333  SVDEBUG << "OSCHandler: Jump only requested, not starting playback" << endl;
334  }
335  }
336 
337  } else if (message.getMethod() == "ffwd") {
338 
339  if (message.getArgCount() == 1) {
340 
341  if (message.getArg(0).canConvert(QVariant::String) &&
342  message.getArg(0).toString() == "similar") {
343 
344  SVDEBUG << "OSCHandler: Calling ffwdSimilar" << endl;
345  ffwdSimilar();
346  }
347  } else {
348 
349  SVDEBUG << "OSCHandler: Calling ffwd" << endl;
350  ffwd();
351  }
352 
353  } else if (message.getMethod() == "rewind") {
354 
355  if (message.getArgCount() == 1) {
356 
357  if (message.getArg(0).canConvert(QVariant::String) &&
358  message.getArg(0).toString() == "similar") {
359 
360  SVDEBUG << "OSCHandler: Calling rewindSimilar" << endl;
361  rewindSimilar();
362  }
363  } else {
364 
365  SVDEBUG << "OSCHandler: Calling rewind" << endl;
366  rewind();
367  }
368 
369  } else if (message.getMethod() == "stop") {
370 
371  if (m_playSource->isPlaying()) {
372  // As with play, we want to use the MainWindow function
373  // rather than call m_playSource directly because that way
374  // the audio driver suspend/resume etc is handled properly
375  SVDEBUG << "OSCHandler: Calling stop" << endl;
376  MainWindow::stop();
377  } else {
378  SVDEBUG << "OSCHandler: Not playing, doing nothing" << endl;
379  }
380 
381  } else if (message.getMethod() == "loop") {
382 
383  if (message.getArgCount() == 1 &&
384  message.getArg(0).canConvert(QVariant::String)) {
385 
386  QString str = message.getArg(0).toString();
387  if (str == "on") {
388  SVDEBUG << "OSCHandler: Enabling loop mode" << endl;
389  m_viewManager->setPlayLoopMode(true);
390  } else if (str == "off") {
391  SVDEBUG << "OSCHandler: Disabling loop mode" << endl;
392  m_viewManager->setPlayLoopMode(false);
393  }
394  }
395 
396  } else if (message.getMethod() == "solo") {
397 
398  if (message.getArgCount() == 1 &&
399  message.getArg(0).canConvert(QVariant::String)) {
400 
401  QString str = message.getArg(0).toString();
402  if (str == "on") {
403  SVDEBUG << "OSCHandler: Enabling solo mode" << endl;
404  m_viewManager->setPlaySoloMode(true);
405  } else if (str == "off") {
406  SVDEBUG << "OSCHandler: Disabling solo mode" << endl;
407  m_viewManager->setPlaySoloMode(false);
408  }
409  }
410 
411  } else if (message.getMethod() == "select" ||
412  message.getMethod() == "addselect") {
413 
414  if (getMainModel()) {
415 
416  sv_frame_t f0 = getMainModel()->getStartFrame();
417  sv_frame_t f1 = getMainModel()->getEndFrame();
418 
419  bool done = false;
420 
421  if (message.getArgCount() == 2 &&
422  message.getArg(0).canConvert(QVariant::Double) &&
423  message.getArg(1).canConvert(QVariant::Double)) {
424 
425  double t0 = message.getArg(0).toDouble();
426  double t1 = message.getArg(1).toDouble();
427  if (t1 < t0) { double temp = t0; t0 = t1; t1 = temp; }
428  if (t0 < 0.0) t0 = 0.0;
429  if (t1 < 0.0) t1 = 0.0;
430 
431  f0 = lrint(t0 * getMainModel()->getSampleRate());
432  f1 = lrint(t1 * getMainModel()->getSampleRate());
433 
434  SVDEBUG << "OSCHandler: Converted selection extents to frames "
435  << f0 << " and " << f1 << endl;
436 
437  Pane *pane = m_paneStack->getCurrentPane();
438  Layer *layer = nullptr;
439  if (pane) layer = pane->getSelectedLayer();
440  if (layer) {
441  int resolution;
442  layer->snapToFeatureFrame(pane, f0, resolution,
443  Layer::SnapLeft, -1);
444  layer->snapToFeatureFrame(pane, f1, resolution,
445  Layer::SnapRight, -1);
446 
447  SVDEBUG << "OSCHandler: Snapped selection extents to "
448  << f0 << " and " << f1 << " for current layer \""
449  << layer->getLayerPresentationName() << "\""
450  << endl;
451  }
452 
453  } else if (message.getArgCount() == 1 &&
454  message.getArg(0).canConvert(QVariant::String)) {
455 
456  QString str = message.getArg(0).toString();
457  if (str == "none") {
458  SVDEBUG << "OSCHandler: Clearing selection" << endl;
459  m_viewManager->clearSelections();
460  done = true;
461  } else if (str == "all") {
462  SVDEBUG << "OSCHandler: Selecting all" << endl;
463  f0 = getModelsStartFrame();
464  f0 = getModelsEndFrame();
465  }
466  }
467 
468  if (!done) {
469  if (message.getMethod() == "select") {
470  m_viewManager->setSelection(Selection(f0, f1));
471  } else {
472  m_viewManager->addSelection(Selection(f0, f1));
473  }
474  }
475 
476  SVDEBUG << "OSCHandler: Selection now is "
477  << m_viewManager->getSelection().toString() << endl;
478 
479  } else {
480  SVCERR << "OSCHandler: No main model, can't modify selection"
481  << endl;
482  }
483 
484  } else if (message.getMethod() == "add") {
485 
486  if (getMainModel()) {
487 
488  if (message.getArgCount() >= 1 &&
489  message.getArg(0).canConvert(QVariant::String)) {
490 
491  int channel = -1;
492  if (message.getArgCount() == 2 &&
493  message.getArg(0).canConvert(QVariant::Int)) {
494  channel = message.getArg(0).toInt();
495  if (channel < -1 ||
496  channel >= int(getMainModel()->getChannelCount())) {
497  SVCERR << "OSCHandler: channel " << channel
498  << " out of range (0 to "
499  << (getMainModel()->getChannelCount() - 1)
500  << ")" << endl;
501  channel = -1;
502  }
503  }
504 
505  QString str = message.getArg(0).toString();
506 
507  LayerFactory::LayerType type =
508  LayerFactory::getInstance()->getLayerTypeForName(str);
509 
510  if (type == LayerFactory::UnknownLayer) {
511  SVCERR << "WARNING: OSCHandler: unknown layer "
512  << "type " << str << endl;
513  } else {
514 
515  LayerConfiguration configuration(type,
516  getMainModelId(),
517  channel);
518 
519  QString pname = LayerFactory::getInstance()->
520  getLayerPresentationName(type);
521 
522  addPane(configuration, tr("Add %1 Pane") .arg(pname));
523 
524  SVDEBUG << "OSCHandler: Added pane \"" << pname
525  << "\"" << endl;
526  }
527  } else {
528  SVCERR << "OSCHandler: Usage: /add <layertype> [<channel>]"
529  << endl;
530  }
531  }
532 
533  } else if (message.getMethod() == "undo") {
534 
535  SVDEBUG << "OSCHandler: Calling undo" << endl;
536  CommandHistory::getInstance()->undo();
537 
538  } else if (message.getMethod() == "redo") {
539 
540  SVDEBUG << "OSCHandler: Calling redo" << endl;
541  CommandHistory::getInstance()->redo();
542 
543  } else if (message.getMethod() == "set") {
544 
545  if (message.getArgCount() == 2 &&
546  message.getArg(0).canConvert(QVariant::String) &&
547  message.getArg(1).canConvert(QVariant::Double)) {
548 
549  QString property = message.getArg(0).toString();
550  float value = (float)message.getArg(1).toDouble();
551 
552  if (property == "gain") {
553  if (value < 0.0) value = 0.0;
554  m_mainLevelPan->setLevel(value);
555  if (m_playTarget) m_playTarget->setOutputGain(value);
556  } else if (property == "speed") {
557  m_playSpeed->setMappedValue(value);
558  } else if (property == "speedup") {
559 
560  // The speedup method existed before the speed method
561  // and is a bit weirder.
562  //
563  // For speed(x), x is a percentage of normal speed, so
564  // x=100 means play at the normal speed, x=50 means
565  // half speed, x=200 double speed etc.
566  //
567  // For speedup(x), x was some sort of modifier of
568  // percentage thing, so x=0 meant play at the normal
569  // speed, x=50 meant play at 150% of normal speed,
570  // x=100 meant play at double speed, and x=-100 rather
571  // bizarrely meant play at half speed. We handle this
572  // now by converting to speed percentage as follows:
573 
574  double percentage = 100.0;
575  if (value > 0.f) {
576  percentage = percentage + value;
577  } else {
578  percentage = 10000.0 / (percentage - value);
579  }
580  SVDEBUG << "OSCHandler: converted speedup(" << value
581  << ") into speed(" << percentage << ")" << endl;
582 
583  m_playSpeed->setMappedValue(percentage);
584 
585  } else if (property == "overlays") {
586  if (value < 0.5) {
587  m_viewManager->setOverlayMode(ViewManager::NoOverlays);
588  } else if (value < 1.5) {
589  m_viewManager->setOverlayMode(ViewManager::StandardOverlays);
590  } else {
591  m_viewManager->setOverlayMode(ViewManager::AllOverlays);
592  }
593  } else if (property == "zoomwheels") {
594  m_viewManager->setZoomWheelsEnabled(value > 0.5);
595  } else if (property == "propertyboxes") {
596  bool toggle = ((value < 0.5) !=
597  (m_paneStack->getLayoutStyle() ==
598  PaneStack::HiddenPropertyStacksLayout));
599  if (toggle) togglePropertyBoxes();
600  }
601 
602  } else {
603  PropertyContainer *container = nullptr;
604  Pane *pane = m_paneStack->getCurrentPane();
605  if (pane &&
606  message.getArgCount() == 3 &&
607  message.getArg(0).canConvert(QVariant::String) &&
608  message.getArg(1).canConvert(QVariant::String) &&
609  message.getArg(2).canConvert(QVariant::String)) {
610  if (message.getArg(0).toString() == "pane") {
611  container = pane->getPropertyContainer(0);
612  } else if (message.getArg(0).toString() == "layer") {
613  container = pane->getSelectedLayer();
614  }
615  } else {
616  SVCERR << "OSCHandler: Usage: /set <control> <value>" << endl
617  << " or /set pane <control> <value>" << endl
618  << " or /set layer <control> <value>" << endl;
619  }
620  if (container) {
621  QString nameString = message.getArg(1).toString();
622  QString valueString = message.getArg(2).toString();
623  Command *c = container->getSetPropertyCommand
624  (nameString, valueString);
625  if (c) CommandHistory::getInstance()->addCommand(c, true, true);
626  }
627  }
628 
629  } else if (message.getMethod() == "setcurrent") {
630 
631  int paneIndex = -1, layerIndex = -1;
632  bool wantLayer = false;
633 
634  if (message.getArgCount() >= 1 &&
635  message.getArg(0).canConvert(QVariant::Int)) {
636 
637  paneIndex = message.getArg(0).toInt() - 1;
638 
639  if (message.getArgCount() >= 2 &&
640  message.getArg(1).canConvert(QVariant::Int)) {
641  wantLayer = true;
642  layerIndex = message.getArg(1).toInt() - 1;
643  }
644  } else {
645  SVCERR << "OSCHandler: Usage: /setcurrent <pane> [<layer>]" << endl;
646  }
647 
648  if (paneIndex >= 0 && paneIndex < m_paneStack->getPaneCount()) {
649  Pane *pane = m_paneStack->getPane(paneIndex);
650  m_paneStack->setCurrentPane(pane);
651  SVDEBUG << "OSCHandler: Set current pane to index "
652  << paneIndex << " (pane id " << pane->getId()
653  << ")" << endl;
654  if (layerIndex >= 0 && layerIndex < pane->getLayerCount()) {
655  Layer *layer = pane->getFixedOrderLayer(layerIndex);
656  m_paneStack->setCurrentLayer(pane, layer);
657  SVDEBUG << "OSCHandler: Set current layer to index "
658  << layerIndex << " (layer \""
659  << layer->getLayerPresentationName() << "\")" << endl;
660  } else if (wantLayer && layerIndex == -1) {
661  m_paneStack->setCurrentLayer(pane, nullptr);
662  } else if (wantLayer) {
663  SVCERR << "OSCHandler: Layer index "
664  << layerIndex << " out of range for pane" << endl;
665  }
666  }
667 
668  } else if (message.getMethod() == "delete") {
669 
670  if (message.getArgCount() == 1 &&
671  message.getArg(0).canConvert(QVariant::String)) {
672 
673  QString target = message.getArg(0).toString();
674 
675  if (target == "pane") {
676 
677  SVDEBUG << "OSCHandler: Calling deleteCurrentPane" << endl;
678  deleteCurrentPane();
679 
680  } else if (target == "layer") {
681 
682  SVDEBUG << "OSCHandler: Calling deleteCurrentLayer" << endl;
683  deleteCurrentLayer();
684 
685  } else {
686 
687  SVCERR << "WARNING: OSCHandler: Unknown delete target \""
688  << target << "\"" << endl;
689  }
690  } else {
691  SVCERR << "OSCHandler: Usage: /delete pane" << endl
692  << " or /delete layer" << endl;
693  }
694 
695  } else if (message.getMethod() == "zoom") {
696 
697  if (message.getArgCount() == 1) {
698  if (message.getArg(0).canConvert(QVariant::String) &&
699  message.getArg(0).toString() == "in") {
700  zoomIn();
701  } else if (message.getArg(0).canConvert(QVariant::String) &&
702  message.getArg(0).toString() == "out") {
703  zoomOut();
704  } else if (message.getArg(0).canConvert(QVariant::String) &&
705  message.getArg(0).toString() == "default") {
706  zoomDefault();
707  } else if (message.getArg(0).canConvert(QVariant::String) &&
708  message.getArg(0).toString() == "fit") {
709  zoomToFit();
710  } else if (message.getArg(0).canConvert(QVariant::Double)) {
711  double level = message.getArg(0).toDouble();
712  Pane *currentPane = m_paneStack->getCurrentPane();
713  ZoomLevel zoomLevel;
714  if (level >= 0.66) {
715  zoomLevel = ZoomLevel(ZoomLevel::FramesPerPixel,
716  int(round(level)));
717  } else {
718  zoomLevel = ZoomLevel(ZoomLevel::PixelsPerFrame,
719  int(round(1.0 / level)));
720  }
721  if (currentPane) {
722  SVDEBUG << "OSCHandler: Setting zoom level to "
723  << zoomLevel << endl;
724  currentPane->setZoomLevel(zoomLevel);
725  } else {
726  SVCERR << "OSCHandler: No current pane, can't set zoom"
727  << endl;
728  }
729  }
730  } else {
731  SVCERR << "OSCHandler: Usage: /zoom <level>" << endl
732  << " or /zoom in" << endl
733  << " or /zoom out" << endl
734  << " or /zoom fit" << endl
735  << " or /zoom default" << endl;
736  }
737 
738  } else if (message.getMethod() == "zoomvertical") {
739 
740  Pane *pane = m_paneStack->getCurrentPane();
741  Layer *layer = nullptr;
742  if (pane && pane->getLayerCount() > 0) {
743  layer = pane->getLayer(pane->getLayerCount() - 1);
744  }
745  int defaultStep = 0;
746  int steps = 0;
747  if (layer) {
748  steps = layer->getVerticalZoomSteps(defaultStep);
749  if (message.getArgCount() == 1 && steps > 0) {
750  if (message.getArg(0).canConvert(QVariant::String) &&
751  message.getArg(0).toString() == "in") {
752  int step = layer->getCurrentVerticalZoomStep() + 1;
753  if (step < steps) layer->setVerticalZoomStep(step);
754  } else if (message.getArg(0).canConvert(QVariant::String) &&
755  message.getArg(0).toString() == "out") {
756  int step = layer->getCurrentVerticalZoomStep() - 1;
757  if (step >= 0) layer->setVerticalZoomStep(step);
758  } else if (message.getArg(0).canConvert(QVariant::String) &&
759  message.getArg(0).toString() == "default") {
760  layer->setVerticalZoomStep(defaultStep);
761  }
762  } else if (message.getArgCount() == 2) {
763  if (message.getArg(0).canConvert(QVariant::Double) &&
764  message.getArg(1).canConvert(QVariant::Double)) {
765  double min = message.getArg(0).toDouble();
766  double max = message.getArg(1).toDouble();
767  layer->setDisplayExtents(min, max);
768  }
769  }
770  }
771 
772  } else if (message.getMethod() == "quit") {
773 
774  SVDEBUG << "OSCHandler: Exiting abruptly" << endl;
775 
776  // discard any more pending OSC messages
777  if (m_oscQueue) {
778  while (!m_oscQueue->isEmpty()) {
779  (void)m_oscQueue->readMessage();
780  }
781  }
782 
783  m_documentModified = false; // so we don't ask to save
784  close();
785 
786  } else if (message.getMethod() == "resize") {
787 
788  if (message.getArgCount() == 2) {
789 
790  int width = 0, height = 0;
791 
792  if (message.getArg(1).canConvert(QVariant::Int)) {
793 
794  height = message.getArg(1).toInt();
795 
796  if (message.getArg(0).canConvert(QVariant::String) &&
797  message.getArg(0).toString() == "pane") {
798 
799  Pane *pane = m_paneStack->getCurrentPane();
800  if (pane) pane->resize(pane->width(), height);
801 
802  } else if (message.getArg(0).canConvert(QVariant::Int)) {
803 
804  width = message.getArg(0).toInt();
805  resize(width, height);
806  }
807  }
808  }
809 
810  } else if (message.getMethod() == "transform") {
811 
812  if (message.getArgCount() == 1 &&
813  message.getArg(0).canConvert(QVariant::String)) {
814 
815  Pane *pane = m_paneStack->getCurrentPane();
816 
817  if (getMainModel() && pane) {
818 
819  TransformId transformId = message.getArg(0).toString();
820 
821  Transform transform = TransformFactory::getInstance()->
822  getDefaultTransformFor(transformId);
823 
824  SVDEBUG << "OSCHandler: Running transform on main model:"
825  << transform.toXmlString() << endl;
826 
827  Layer *newLayer = m_document->createDerivedLayer
828  (transform, getMainModelId());
829 
830  if (newLayer) {
831  m_document->addLayerToView(pane, newLayer);
832  m_recentTransforms.add(transformId);
833  m_paneStack->setCurrentLayer(pane, newLayer);
834  } else {
835  SVCERR << "OSCHandler: Transform failed to run" << endl;
836  }
837  } else {
838  SVCERR << "OSCHandler: Lack main model or pane, "
839  << "can't run transform" << endl;
840  }
841  }
842 
843  } else {
844  SVCERR << "WARNING: OSCHandler: Unknown or unsupported "
845  << "method \"" << message.getMethod()
846  << "\"" << endl;
847  }
848 
849  SVDEBUG << "OSCHandler at " << NOW << ": finished message: "
850  << message.toString() << " in " << timer.elapsed() << "ms" << endl;
851 }
#define NOW
Definition: OSCHandler.cpp:42
LevelPanToolButton * m_mainLevelPan
Definition: MainWindow.h:176
virtual void addPane()
AudioDial * m_playSpeed
Definition: MainWindow.h:177
void handleOSCMessage(const OSCMessage &) override
Definition: OSCHandler.cpp:46