annotate main/OSCHandler.cpp @ 2596:04d381f0d89a tip

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