annotate main/OSCHandler.cpp @ 2560:cbde01e5c626 smoother-recording

Add record-mono setting
author Chris Cannam
date Tue, 16 Jun 2020 17:04:54 +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 }