annotate main/MainWindow.cpp @ 698:ee97c742d184 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:43 +0100
parents 0a78db6145b2
children
rev   line source
Chris@0 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@0 4 Tony
Chris@0 5 An intonation analysis and annotation tool
Chris@0 6 Centre for Digital Music, Queen Mary, University of London.
Chris@0 7 This file copyright 2006-2012 Chris Cannam and QMUL.
Chris@0 8
Chris@0 9 This program is free software; you can redistribute it and/or
Chris@0 10 modify it under the terms of the GNU General Public License as
Chris@0 11 published by the Free Software Foundation; either version 2 of the
Chris@0 12 License, or (at your option) any later version. See the file
Chris@0 13 COPYING included with this distribution for more information.
Chris@0 14 */
Chris@0 15
Chris@0 16 #include "../version.h"
Chris@0 17
Chris@0 18 #include "MainWindow.h"
Chris@95 19 #include "NetworkPermissionTester.h"
Chris@6 20 #include "Analyser.h"
Chris@6 21
Chris@0 22 #include "framework/Document.h"
Chris@95 23 #include "framework/VersionTester.h"
Chris@0 24
Chris@0 25 #include "view/Pane.h"
Chris@0 26 #include "view/PaneStack.h"
Chris@0 27 #include "data/model/WaveFileModel.h"
matthiasm@26 28 #include "data/model/NoteModel.h"
matthiasm@42 29 #include "layer/FlexiNoteLayer.h"
Chris@0 30 #include "view/ViewManager.h"
Chris@0 31 #include "base/Preferences.h"
Chris@539 32 #include "base/RecordDirectory.h"
Chris@404 33 #include "base/AudioLevel.h"
Chris@0 34 #include "layer/WaveformLayer.h"
Chris@0 35 #include "layer/TimeInstantLayer.h"
Chris@0 36 #include "layer/TimeValueLayer.h"
Chris@192 37 #include "layer/SpectrogramLayer.h"
Chris@0 38 #include "widgets/Fader.h"
Chris@0 39 #include "view/Overview.h"
Chris@0 40 #include "widgets/AudioDial.h"
Chris@0 41 #include "widgets/IconLoader.h"
Chris@0 42 #include "widgets/KeyReference.h"
Chris@414 43 #include "widgets/LevelPanToolButton.h"
Chris@474 44 #include "audio/AudioCallbackPlaySource.h"
Chris@521 45 #include "audio/AudioCallbackRecordTarget.h"
Chris@474 46 #include "audio/PlaySpeedRangeMapper.h"
Chris@0 47 #include "base/Profiler.h"
Chris@0 48 #include "base/UnitDatabase.h"
Chris@0 49 #include "layer/ColourDatabase.h"
Chris@139 50 #include "base/Selection.h"
Chris@0 51
Chris@174 52 #include "rdf/RDFImporter.h"
Chris@174 53 #include "data/fileio/DataFileReaderFactory.h"
Chris@174 54 #include "data/fileio/CSVFormat.h"
matthiasm@26 55 #include "data/fileio/CSVFileWriter.h"
matthiasm@26 56 #include "data/fileio/MIDIFileWriter.h"
matthiasm@26 57 #include "rdf/RDFExporter.h"
matthiasm@26 58
Chris@227 59 #include "widgets/RangeInputDialog.h"
Chris@244 60 #include "widgets/ActivityLog.h"
Chris@227 61
Chris@0 62 // For version information
Chris@0 63 #include "vamp/vamp.h"
Chris@0 64 #include "vamp-sdk/PluginBase.h"
Chris@0 65 #include "plugin/api/ladspa.h"
Chris@0 66 #include "plugin/api/dssi.h"
Chris@0 67
Chris@474 68 #include <bqaudioio/SystemPlaybackTarget.h>
Chris@479 69 #include <bqaudioio/SystemAudioIO.h>
Chris@474 70
Chris@0 71 #include <QApplication>
Chris@0 72 #include <QMessageBox>
Chris@0 73 #include <QGridLayout>
Chris@0 74 #include <QLabel>
Chris@0 75 #include <QMenuBar>
Chris@0 76 #include <QToolBar>
Chris@0 77 #include <QToolButton>
Chris@0 78 #include <QInputDialog>
Chris@0 79 #include <QStatusBar>
Chris@0 80 #include <QFileInfo>
Chris@0 81 #include <QDir>
Chris@0 82 #include <QProcess>
Chris@2 83 #include <QPushButton>
Chris@0 84 #include <QSettings>
Chris@0 85 #include <QScrollArea>
Chris@128 86 #include <QPainter>
Chris@413 87 #include <QWidgetAction>
Chris@585 88 #include <QTextEdit>
Chris@585 89 #include <QDialogButtonBox>
Chris@0 90
Chris@0 91 #include <iostream>
Chris@0 92 #include <cstdio>
Chris@0 93 #include <errno.h>
Chris@0 94
Chris@0 95 using std::vector;
Chris@0 96
Chris@0 97
Chris@635 98 MainWindow::MainWindow(AudioMode audioMode,
Chris@635 99 bool withSonification,
Chris@635 100 bool withSpectrogram) :
Chris@635 101 MainWindowBase(audioMode,
Chris@635 102 MainWindowBase::MIDI_NONE,
Chris@604 103 int(PaneStack::Option::NoPropertyStacks) |
Chris@604 104 int(PaneStack::Option::NoPaneAccessories)),
Chris@0 105 m_overview(0),
Chris@0 106 m_mainMenusCreated(false),
Chris@0 107 m_playbackMenu(0),
gyorgyf@45 108 m_recentFilesMenu(0),
Chris@0 109 m_rightButtonMenu(0),
Chris@0 110 m_rightButtonPlaybackMenu(0),
Chris@0 111 m_deleteSelectedAction(0),
Chris@0 112 m_ffwdAction(0),
Chris@0 113 m_rwdAction(0),
Chris@168 114 m_intelligentActionOn(true), //GF: !!! temporary
Chris@244 115 m_activityLog(new ActivityLog()),
matthiasm@296 116 m_keyReference(new KeyReference()),
matthiasm@364 117 m_selectionAnchor(0),
matthiasm@364 118 m_withSonification(withSonification),
matthiasm@364 119 m_withSpectrogram(withSpectrogram)
Chris@0 120 {
Chris@0 121 setWindowTitle(QApplication::applicationName());
Chris@0 122
Chris@206 123 #ifdef Q_OS_MAC
Chris@206 124 #if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
Chris@206 125 setUnifiedTitleAndToolBarOnMac(true);
Chris@206 126 #endif
Chris@206 127 #endif
Chris@206 128
Chris@0 129 UnitDatabase *udb = UnitDatabase::getInstance();
Chris@0 130 udb->registerUnit("Hz");
Chris@0 131 udb->registerUnit("dB");
Chris@0 132 udb->registerUnit("s");
Chris@0 133
Chris@0 134 ColourDatabase *cdb = ColourDatabase::getInstance();
Chris@0 135 cdb->addColour(Qt::black, tr("Black"));
Chris@0 136 cdb->addColour(Qt::darkRed, tr("Red"));
Chris@0 137 cdb->addColour(Qt::darkBlue, tr("Blue"));
Chris@0 138 cdb->addColour(Qt::darkGreen, tr("Green"));
Chris@0 139 cdb->addColour(QColor(200, 50, 255), tr("Purple"));
Chris@0 140 cdb->addColour(QColor(255, 150, 50), tr("Orange"));
Chris@120 141 cdb->addColour(QColor(180, 180, 180), tr("Grey"));
Chris@0 142 cdb->setUseDarkBackground(cdb->addColour(Qt::white, tr("White")), true);
Chris@0 143 cdb->setUseDarkBackground(cdb->addColour(Qt::red, tr("Bright Red")), true);
Chris@0 144 cdb->setUseDarkBackground(cdb->addColour(QColor(30, 150, 255), tr("Bright Blue")), true);
Chris@0 145 cdb->setUseDarkBackground(cdb->addColour(Qt::green, tr("Bright Green")), true);
Chris@0 146 cdb->setUseDarkBackground(cdb->addColour(QColor(225, 74, 255), tr("Bright Purple")), true);
Chris@0 147 cdb->setUseDarkBackground(cdb->addColour(QColor(255, 188, 80), tr("Bright Orange")), true);
Chris@0 148
Chris@0 149 Preferences::getInstance()->setResampleOnLoad(true);
Chris@315 150 Preferences::getInstance()->setFixedSampleRate(44100);
Chris@0 151 Preferences::getInstance()->setSpectrogramSmoothing
Chris@0 152 (Preferences::SpectrogramInterpolated);
Chris@332 153 Preferences::getInstance()->setNormaliseAudio(true);
Chris@0 154
Chris@0 155 QSettings settings;
Chris@0 156
Chris@0 157 settings.beginGroup("MainWindow");
Chris@0 158 settings.setValue("showstatusbar", false);
Chris@0 159 settings.endGroup();
Chris@0 160
Chris@96 161 settings.beginGroup("Transformer");
Chris@96 162 settings.setValue("use-flexi-note-model", true);
Chris@96 163 settings.endGroup();
Chris@96 164
Chris@73 165 settings.beginGroup("LayerDefaults");
Chris@73 166 settings.setValue("waveform",
Chris@73 167 QString("<layer scale=\"%1\" channelMode=\"%2\"/>")
Chris@73 168 .arg(int(WaveformLayer::LinearScale))
Chris@73 169 .arg(int(WaveformLayer::MixChannels)));
Chris@73 170 settings.endGroup();
Chris@73 171
Chris@6 172 m_viewManager->setAlignMode(false);
Chris@6 173 m_viewManager->setPlaySoloMode(false);
Chris@392 174 m_viewManager->setToolMode(ViewManager::NavigateMode);
Chris@0 175 m_viewManager->setZoomWheelsEnabled(false);
Chris@6 176 m_viewManager->setIlluminateLocalFeatures(true);
matthiasm@368 177 m_viewManager->setShowWorkTitle(false);
Chris@6 178 m_viewManager->setShowCentreLine(false);
matthiasm@368 179 m_viewManager->setShowDuration(false);
Chris@213 180 m_viewManager->setOverlayMode(ViewManager::GlobalOverlays);
Chris@0 181
Chris@192 182 connect(m_viewManager, SIGNAL(selectionChangedByUser()),
Chris@192 183 this, SLOT(selectionChangedByUser()));
Chris@164 184
Chris@0 185 QFrame *frame = new QFrame;
Chris@0 186 setCentralWidget(frame);
Chris@0 187
Chris@0 188 QGridLayout *layout = new QGridLayout;
Chris@0 189
Chris@0 190 QScrollArea *scroll = new QScrollArea(frame);
Chris@0 191 scroll->setWidgetResizable(true);
Chris@0 192 scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
Chris@0 193 scroll->setFrameShape(QFrame::NoFrame);
Chris@0 194
Chris@6 195 // We have a pane stack: it comes with the territory. However, we
Chris@120 196 // have a fixed and known number of panes in it -- it isn't
Chris@120 197 // variable
Chris@399 198 connect(m_paneStack, SIGNAL(doubleClickSelectInvoked(sv_frame_t)),
Chris@399 199 this, SLOT(doubleClickSelectInvoked(sv_frame_t)));
Chris@0 200 scroll->setWidget(m_paneStack);
Chris@6 201
Chris@0 202 m_overview = new Overview(frame);
Chris@362 203 m_overview->setPlaybackFollow(PlaybackScrollPage);
Chris@0 204 m_overview->setViewManager(m_viewManager);
Chris@393 205 m_overview->setFixedHeight(60);
Chris@0 206 #ifndef _WIN32
Chris@0 207 // For some reason, the contents of the overview never appear if we
Chris@0 208 // make this setting on Windows. I have no inclination at the moment
Chris@0 209 // to track down the reason why.
Chris@0 210 m_overview->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
Chris@0 211 #endif
Chris@0 212 connect(m_overview, SIGNAL(contextHelpChanged(const QString &)),
Chris@0 213 this, SLOT(contextHelpChanged(const QString &)));
Chris@0 214
Chris@0 215 m_panLayer = new WaveformLayer;
Chris@0 216 m_panLayer->setChannelMode(WaveformLayer::MergeChannels);
Chris@0 217 m_panLayer->setAggressiveCacheing(true);
Chris@393 218 m_panLayer->setGain(0.5);
Chris@0 219 m_overview->addLayer(m_panLayer);
Chris@0 220
Chris@0 221 if (m_viewManager->getGlobalDarkBackground()) {
Chris@0 222 m_panLayer->setBaseColour
Chris@0 223 (ColourDatabase::getInstance()->getColourIndex(tr("Bright Green")));
Chris@0 224 } else {
Chris@0 225 m_panLayer->setBaseColour
matthiasm@13 226 (ColourDatabase::getInstance()->getColourIndex(tr("Blue")));
Chris@0 227 }
Chris@0 228
Chris@0 229 m_fader = new Fader(frame, false);
Chris@0 230 connect(m_fader, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
Chris@0 231 connect(m_fader, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
Chris@0 232
Chris@0 233 m_playSpeed = new AudioDial(frame);
Chris@343 234 m_playSpeed->setMeterColor(Qt::darkBlue);
Chris@0 235 m_playSpeed->setMinimum(0);
Chris@477 236 m_playSpeed->setMaximum(120);
Chris@502 237 m_playSpeed->setValue(60);
Chris@0 238 m_playSpeed->setFixedWidth(24);
Chris@0 239 m_playSpeed->setFixedHeight(24);
Chris@0 240 m_playSpeed->setNotchesVisible(true);
Chris@0 241 m_playSpeed->setPageStep(10);
Chris@477 242 m_playSpeed->setObjectName(tr("Playback Speed"));
Chris@477 243 m_playSpeed->setDefaultValue(60);
Chris@474 244 m_playSpeed->setRangeMapper(new PlaySpeedRangeMapper);
Chris@0 245 m_playSpeed->setShowToolTip(true);
Chris@0 246 connect(m_playSpeed, SIGNAL(valueChanged(int)),
gyorgyf@27 247 this, SLOT(playSpeedChanged(int)));
Chris@0 248 connect(m_playSpeed, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
Chris@0 249 connect(m_playSpeed, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
Chris@0 250
Chris@414 251 m_audioLPW = new LevelPanToolButton(frame);
Chris@434 252 m_audioLPW->setIncludeMute(false);
Chris@404 253 m_audioLPW->setObjectName(tr("Audio Track Level and Pan"));
Chris@404 254 connect(m_audioLPW, SIGNAL(levelChanged(float)), this, SLOT(audioGainChanged(float)));
Chris@404 255 connect(m_audioLPW, SIGNAL(panChanged(float)), this, SLOT(audioPanChanged(float)));
Chris@404 256
Chris@404 257 if (m_withSonification) {
Chris@404 258
Chris@414 259 m_pitchLPW = new LevelPanToolButton(frame);
Chris@434 260 m_pitchLPW->setIncludeMute(false);
Chris@404 261 m_pitchLPW->setObjectName(tr("Pitch Track Level and Pan"));
Chris@404 262 connect(m_pitchLPW, SIGNAL(levelChanged(float)), this, SLOT(pitchGainChanged(float)));
Chris@404 263 connect(m_pitchLPW, SIGNAL(panChanged(float)), this, SLOT(pitchPanChanged(float)));
Chris@404 264
Chris@414 265 m_notesLPW = new LevelPanToolButton(frame);
Chris@434 266 m_notesLPW->setIncludeMute(false);
Chris@404 267 m_notesLPW->setObjectName(tr("Note Track Level and Pan"));
Chris@404 268 connect(m_notesLPW, SIGNAL(levelChanged(float)), this, SLOT(notesGainChanged(float)));
Chris@404 269 connect(m_notesLPW, SIGNAL(panChanged(float)), this, SLOT(notesPanChanged(float)));
matthiasm@366 270 }
justin@160 271
Chris@0 272 layout->setSpacing(4);
Chris@6 273 layout->addWidget(m_overview, 0, 1);
Chris@6 274 layout->addWidget(scroll, 1, 1);
Chris@0 275
Chris@0 276 layout->setColumnStretch(1, 10);
Chris@0 277
Chris@0 278 frame->setLayout(layout);
Chris@0 279
gyorgyf@21 280 m_analyser = new Analyser();
Chris@128 281 connect(m_analyser, SIGNAL(layersChanged()),
Chris@128 282 this, SLOT(updateLayerStatuses()));
Chris@187 283 connect(m_analyser, SIGNAL(layersChanged()),
Chris@187 284 this, SLOT(updateMenuStates()));
gyorgyf@21 285
Chris@0 286 setupMenus();
Chris@0 287 setupToolbars();
Chris@0 288 setupHelpMenu();
Chris@0 289
Chris@0 290 statusBar();
Chris@0 291
Chris@288 292 finaliseMenus();
Chris@288 293
Chris@244 294 connect(m_viewManager, SIGNAL(activity(QString)),
Chris@244 295 m_activityLog, SLOT(activityHappened(QString)));
Chris@244 296 connect(m_playSource, SIGNAL(activity(QString)),
Chris@244 297 m_activityLog, SLOT(activityHappened(QString)));
Chris@244 298 connect(CommandHistory::getInstance(), SIGNAL(activity(QString)),
Chris@244 299 m_activityLog, SLOT(activityHappened(QString)));
Chris@244 300 connect(this, SIGNAL(activity(QString)),
Chris@244 301 m_activityLog, SLOT(activityHappened(QString)));
Chris@244 302 connect(this, SIGNAL(replacedDocument()), this, SLOT(documentReplaced()));
Chris@260 303 connect(this, SIGNAL(sessionLoaded()), this, SLOT(analyseNewMainModel()));
Chris@260 304 connect(this, SIGNAL(audioFileLoaded()), this, SLOT(analyseNewMainModel()));
Chris@244 305 m_activityLog->hide();
Chris@244 306
Chris@485 307 setAudioRecordMode(RecordReplaceSession);
Chris@485 308
Chris@95 309 newSession();
Chris@6 310
Chris@95 311 settings.beginGroup("MainWindow");
Chris@95 312 settings.setValue("zoom-default", 512);
Chris@95 313 settings.endGroup();
Chris@95 314 zoomDefault();
Chris@95 315
Chris@95 316 NetworkPermissionTester tester;
Chris@95 317 bool networkPermission = tester.havePermission();
Chris@95 318 if (networkPermission) {
Chris@95 319 m_versionTester = new VersionTester
Chris@95 320 ("sonicvisualiser.org", "latest-tony-version.txt", TONY_VERSION);
Chris@95 321 connect(m_versionTester, SIGNAL(newerVersionAvailable(QString)),
Chris@95 322 this, SLOT(newerVersionAvailable(QString)));
Chris@95 323 } else {
Chris@95 324 m_versionTester = 0;
Chris@95 325 }
Chris@0 326 }
Chris@0 327
Chris@0 328 MainWindow::~MainWindow()
Chris@0 329 {
Chris@6 330 delete m_analyser;
Chris@0 331 delete m_keyReference;
Chris@0 332 Profiles::getInstance()->dump();
Chris@0 333 }
Chris@0 334
Chris@0 335 void
Chris@0 336 MainWindow::setupMenus()
Chris@0 337 {
Chris@0 338 if (!m_mainMenusCreated) {
Chris@385 339
Chris@385 340 #ifdef Q_OS_LINUX
Chris@385 341 // In Ubuntu 14.04 the window's menu bar goes missing entirely
Chris@385 342 // if the user is running any desktop environment other than Unity
Chris@385 343 // (in which the faux single-menubar appears). The user has a
Chris@385 344 // workaround, to remove the appmenu-qt5 package, but that is
Chris@385 345 // awkward and the problem is so severe that it merits disabling
Chris@385 346 // the system menubar integration altogether. Like this:
Chris@385 347 menuBar()->setNativeMenuBar(false);
Chris@385 348 #endif
Chris@385 349
Chris@0 350 m_rightButtonMenu = new QMenu();
Chris@0 351 }
Chris@0 352
Chris@0 353 if (!m_mainMenusCreated) {
Chris@0 354 CommandHistory::getInstance()->registerMenu(m_rightButtonMenu);
Chris@0 355 m_rightButtonMenu->addSeparator();
Chris@0 356 }
Chris@0 357
Chris@0 358 setupFileMenu();
Chris@0 359 setupEditMenu();
Chris@0 360 setupViewMenu();
matthiasm@317 361 setupAnalysisMenu();
Chris@0 362
Chris@0 363 m_mainMenusCreated = true;
Chris@0 364 }
Chris@0 365
Chris@0 366 void
Chris@0 367 MainWindow::setupFileMenu()
Chris@0 368 {
Chris@0 369 if (m_mainMenusCreated) return;
Chris@0 370
Chris@0 371 QMenu *menu = menuBar()->addMenu(tr("&File"));
Chris@0 372 menu->setTearOffEnabled(true);
Chris@0 373 QToolBar *toolbar = addToolBar(tr("File Toolbar"));
Chris@0 374
Chris@0 375 m_keyReference->setCategory(tr("File and Session Management"));
Chris@0 376
Chris@0 377 IconLoader il;
Chris@1 378 QIcon icon;
Chris@1 379 QAction *action;
Chris@0 380
Chris@0 381 icon = il.load("fileopen");
Chris@1 382 action = new QAction(icon, tr("&Open..."), this);
Chris@0 383 action->setShortcut(tr("Ctrl+O"));
Chris@257 384 action->setStatusTip(tr("Open a session or audio file"));
Chris@0 385 connect(action, SIGNAL(triggered()), this, SLOT(openFile()));
Chris@0 386 m_keyReference->registerShortcut(action);
Chris@0 387 menu->addAction(action);
Chris@0 388 toolbar->addAction(action);
Chris@0 389
Chris@1 390 action = new QAction(tr("Open Lo&cation..."), this);
Chris@0 391 action->setShortcut(tr("Ctrl+Shift+O"));
Chris@1 392 action->setStatusTip(tr("Open a file from a remote URL"));
Chris@0 393 connect(action, SIGNAL(triggered()), this, SLOT(openLocation()));
Chris@0 394 m_keyReference->registerShortcut(action);
Chris@0 395 menu->addAction(action);
Chris@0 396
Chris@1 397 m_recentFilesMenu = menu->addMenu(tr("Open &Recent"));
Chris@0 398 m_recentFilesMenu->setTearOffEnabled(true);
Chris@0 399 setupRecentFilesMenu();
Chris@0 400 connect(&m_recentFiles, SIGNAL(recentChanged()),
Chris@0 401 this, SLOT(setupRecentFilesMenu()));
Chris@0 402
Chris@0 403 menu->addSeparator();
Chris@257 404
Chris@257 405 icon = il.load("filesave");
Chris@257 406 action = new QAction(icon, tr("&Save Session"), this);
Chris@257 407 action->setShortcut(tr("Ctrl+S"));
Chris@257 408 action->setStatusTip(tr("Save the current session into a %1 session file").arg(QApplication::applicationName()));
Chris@257 409 connect(action, SIGNAL(triggered()), this, SLOT(saveSession()));
Chris@257 410 connect(this, SIGNAL(canSave(bool)), action, SLOT(setEnabled(bool)));
Chris@257 411 m_keyReference->registerShortcut(action);
Chris@257 412 menu->addAction(action);
Chris@257 413 toolbar->addAction(action);
Chris@257 414
Chris@257 415 icon = il.load("filesaveas");
Chris@257 416 action = new QAction(icon, tr("Save Session &As..."), this);
Chris@257 417 action->setShortcut(tr("Ctrl+Shift+S"));
Chris@257 418 action->setStatusTip(tr("Save the current session into a new %1 session file").arg(QApplication::applicationName()));
Chris@257 419 connect(action, SIGNAL(triggered()), this, SLOT(saveSessionAs()));
Chris@313 420 connect(this, SIGNAL(canSaveAs(bool)), action, SLOT(setEnabled(bool)));
Chris@257 421 menu->addAction(action);
Chris@257 422 toolbar->addAction(action);
Chris@257 423
Chris@486 424 action = new QAction(tr("Save Session to Audio File &Path"), this);
matthiasm@310 425 action->setShortcut(tr("Ctrl+Alt+S"));
Chris@313 426 action->setStatusTip(tr("Save the current session into a %1 session file with the same filename as the audio but a .ton extension.").arg(QApplication::applicationName()));
matthiasm@310 427 connect(action, SIGNAL(triggered()), this, SLOT(saveSessionInAudioPath()));
Chris@313 428 connect(this, SIGNAL(canSaveAs(bool)), action, SLOT(setEnabled(bool)));
matthiasm@310 429 menu->addAction(action);
matthiasm@310 430
Chris@257 431 menu->addSeparator();
Chris@257 432
Chris@253 433 action = new QAction(tr("I&mport Pitch Track Data..."), this);
Chris@253 434 action->setStatusTip(tr("Import pitch-track data from a CSV, RDF, or layer XML file"));
Chris@174 435 connect(action, SIGNAL(triggered()), this, SLOT(importPitchLayer()));
Chris@174 436 connect(this, SIGNAL(canImportLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@174 437 menu->addAction(action);
Chris@174 438
Chris@253 439 action = new QAction(tr("E&xport Pitch Track Data..."), this);
Chris@253 440 action->setStatusTip(tr("Export pitch-track data to a CSV, RDF, or layer XML file"));
Chris@85 441 connect(action, SIGNAL(triggered()), this, SLOT(exportPitchLayer()));
Chris@224 442 connect(this, SIGNAL(canExportPitchTrack(bool)), action, SLOT(setEnabled(bool)));
Chris@85 443 menu->addAction(action);
Chris@85 444
Chris@253 445 action = new QAction(tr("&Export Note Data..."), this);
Chris@253 446 action->setStatusTip(tr("Export note data to a CSV, RDF, layer XML, or MIDI file"));
Chris@85 447 connect(action, SIGNAL(triggered()), this, SLOT(exportNoteLayer()));
Chris@224 448 connect(this, SIGNAL(canExportNotes(bool)), action, SLOT(setEnabled(bool)));
matthiasm@26 449 menu->addAction(action);
matthiasm@26 450
matthiasm@26 451 menu->addSeparator();
Chris@485 452
Chris@485 453 action = new QAction(tr("Browse Recorded Audio"), this);
Chris@485 454 action->setStatusTip(tr("Open the Recorded Audio folder in the system file browser"));
Chris@485 455 connect(action, SIGNAL(triggered()), this, SLOT(browseRecordedAudio()));
Chris@485 456 menu->addAction(action);
Chris@485 457
Chris@485 458 menu->addSeparator();
Chris@485 459
Chris@0 460 action = new QAction(il.load("exit"), tr("&Quit"), this);
Chris@0 461 action->setShortcut(tr("Ctrl+Q"));
Chris@0 462 action->setStatusTip(tr("Exit %1").arg(QApplication::applicationName()));
Chris@0 463 connect(action, SIGNAL(triggered()), this, SLOT(close()));
Chris@0 464 m_keyReference->registerShortcut(action);
Chris@0 465 menu->addAction(action);
Chris@0 466 }
Chris@0 467
Chris@0 468 void
Chris@0 469 MainWindow::setupEditMenu()
Chris@0 470 {
Chris@0 471 if (m_mainMenusCreated) return;
Chris@0 472
Chris@0 473 QMenu *menu = menuBar()->addMenu(tr("&Edit"));
Chris@0 474 menu->setTearOffEnabled(true);
Chris@0 475 CommandHistory::getInstance()->registerMenu(menu);
Chris@126 476 menu->addSeparator();
Chris@126 477
Chris@254 478 m_keyReference->setCategory
Chris@254 479 (tr("Selection Strip Mouse Actions"));
Chris@254 480 m_keyReference->registerShortcut
Chris@254 481 (tr("Jump"), tr("Left"),
Chris@254 482 tr("Click left button to move the playback position to a time"));
Chris@254 483 m_keyReference->registerShortcut
Chris@254 484 (tr("Select"), tr("Left"),
Chris@254 485 tr("Click left button and drag to select a region of time"));
Chris@254 486 m_keyReference->registerShortcut
Chris@254 487 (tr("Select Note Duration"), tr("Double-Click Left"),
Chris@254 488 tr("Double-click left button to select the region of time corresponding to a note"));
Chris@254 489
Chris@126 490 QToolBar *toolbar = addToolBar(tr("Tools Toolbar"));
Chris@126 491
Chris@126 492 CommandHistory::getInstance()->registerToolbar(toolbar);
Chris@126 493
Chris@250 494 QActionGroup *group = new QActionGroup(this);
Chris@250 495
Chris@250 496 IconLoader il;
Chris@250 497
Chris@126 498 m_keyReference->setCategory(tr("Tool Selection"));
Chris@126 499 QAction *action = toolbar->addAction(il.load("navigate"),
Chris@126 500 tr("Navigate"));
Chris@126 501 action->setCheckable(true);
Chris@126 502 action->setChecked(true);
Chris@126 503 action->setShortcut(tr("1"));
Chris@126 504 action->setStatusTip(tr("Navigate"));
Chris@126 505 connect(action, SIGNAL(triggered()), this, SLOT(toolNavigateSelected()));
Chris@126 506 connect(this, SIGNAL(replacedDocument()), action, SLOT(trigger()));
Chris@126 507 group->addAction(action);
Chris@126 508 menu->addAction(action);
Chris@126 509 m_keyReference->registerShortcut(action);
Chris@126 510
Chris@250 511 m_keyReference->setCategory
Chris@250 512 (tr("Navigate Tool Mouse Actions"));
Chris@250 513 m_keyReference->registerShortcut
Chris@250 514 (tr("Navigate"), tr("Left"),
Chris@250 515 tr("Click left button and drag to move around"));
Chris@250 516 m_keyReference->registerShortcut
Chris@250 517 (tr("Re-Analyse Area"), tr("Shift+Left"),
Chris@250 518 tr("Shift-click left button and drag to define a specific pitch and time range to re-analyse"));
Chris@250 519 m_keyReference->registerShortcut
Chris@250 520 (tr("Edit"), tr("Double-Click Left"),
Chris@250 521 tr("Double-click left button on an item to edit it"));
Chris@392 522
Chris@250 523 m_keyReference->setCategory(tr("Tool Selection"));
Chris@126 524 action = toolbar->addAction(il.load("move"),
Chris@126 525 tr("Edit"));
Chris@126 526 action->setCheckable(true);
Chris@126 527 action->setShortcut(tr("2"));
Chris@126 528 action->setStatusTip(tr("Edit with Note Intelligence"));
Chris@126 529 connect(action, SIGNAL(triggered()), this, SLOT(toolEditSelected()));
Chris@126 530 group->addAction(action);
Chris@126 531 menu->addAction(action);
Chris@126 532 m_keyReference->registerShortcut(action);
Chris@126 533
Chris@250 534 m_keyReference->setCategory
Chris@250 535 (tr("Note Edit Tool Mouse Actions"));
Chris@250 536 m_keyReference->registerShortcut
Chris@250 537 (tr("Adjust Pitch"), tr("Left"),
Chris@250 538 tr("Click left button on the main part of a note and drag to move it up or down"));
Chris@250 539 m_keyReference->registerShortcut
Chris@250 540 (tr("Split"), tr("Left"),
Chris@250 541 tr("Click left button on the bottom edge of a note to split it at the click point"));
Chris@250 542 m_keyReference->registerShortcut
Chris@250 543 (tr("Resize"), tr("Left"),
Chris@250 544 tr("Click left button on the left or right edge of a note and drag to change the time or duration of the note"));
Chris@250 545 m_keyReference->registerShortcut
Chris@250 546 (tr("Erase"), tr("Shift+Left"),
Chris@250 547 tr("Shift-click left button on a note to remove it"));
Chris@250 548
Chris@250 549
Chris@126 550 /* Remove for now...
Chris@126 551
Chris@250 552 m_keyReference->setCategory(tr("Tool Selection"));
Chris@126 553 action = toolbar->addAction(il.load("notes"),
Chris@126 554 tr("Free Edit"));
Chris@126 555 action->setCheckable(true);
Chris@126 556 action->setShortcut(tr("3"));
Chris@126 557 action->setStatusTip(tr("Free Edit"));
Chris@126 558 connect(action, SIGNAL(triggered()), this, SLOT(toolFreeEditSelected()));
Chris@126 559 group->addAction(action);
Chris@126 560 m_keyReference->registerShortcut(action);
Chris@126 561 */
Chris@142 562
Chris@142 563 menu->addSeparator();
Chris@142 564
Chris@143 565 m_keyReference->setCategory(tr("Selection"));
Chris@143 566
Chris@143 567 action = new QAction(tr("Select &All"), this);
Chris@143 568 action->setShortcut(tr("Ctrl+A"));
Chris@143 569 action->setStatusTip(tr("Select the whole duration of the current session"));
Chris@143 570 connect(action, SIGNAL(triggered()), this, SLOT(selectAll()));
Chris@143 571 connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool)));
Chris@143 572 m_keyReference->registerShortcut(action);
Chris@143 573 menu->addAction(action);
Chris@143 574 m_rightButtonMenu->addAction(action);
Chris@143 575
Chris@287 576 action = new QAction(tr("C&lear Selection"), this);
Chris@211 577 action->setShortcuts(QList<QKeySequence>()
Chris@211 578 << QKeySequence(tr("Esc"))
Chris@211 579 << QKeySequence(tr("Ctrl+Esc")));
Chris@211 580 action->setStatusTip(tr("Clear the selection and abandon any pending pitch choices in it"));
Chris@194 581 connect(action, SIGNAL(triggered()), this, SLOT(abandonSelection()));
Chris@143 582 connect(this, SIGNAL(canClearSelection(bool)), action, SLOT(setEnabled(bool)));
Chris@143 583 m_keyReference->registerShortcut(action);
Chris@211 584 m_keyReference->registerAlternativeShortcut(action, QKeySequence(tr("Ctrl+Esc")));
Chris@143 585 menu->addAction(action);
Chris@143 586 m_rightButtonMenu->addAction(action);
Chris@143 587
Chris@143 588 menu->addSeparator();
Chris@189 589 m_rightButtonMenu->addSeparator();
Chris@143 590
Chris@189 591 m_keyReference->setCategory(tr("Pitch Track"));
Chris@211 592
Chris@211 593 action = new QAction(tr("Choose Higher Pitch"), this);
Chris@211 594 action->setShortcut(tr("Ctrl+Up"));
Chris@211 595 action->setStatusTip(tr("Move pitches up an octave, or to the next higher pitch candidate"));
Chris@211 596 m_keyReference->registerShortcut(action);
Chris@211 597 connect(action, SIGNAL(triggered()), this, SLOT(switchPitchUp()));
Chris@211 598 connect(this, SIGNAL(canClearSelection(bool)), action, SLOT(setEnabled(bool)));
Chris@211 599 menu->addAction(action);
Chris@211 600 m_rightButtonMenu->addAction(action);
Chris@211 601
Chris@211 602 action = new QAction(tr("Choose Lower Pitch"), this);
Chris@211 603 action->setShortcut(tr("Ctrl+Down"));
Chris@211 604 action->setStatusTip(tr("Move pitches down an octave, or to the next lower pitch candidate"));
Chris@211 605 m_keyReference->registerShortcut(action);
Chris@211 606 connect(action, SIGNAL(triggered()), this, SLOT(switchPitchDown()));
Chris@211 607 connect(this, SIGNAL(canClearSelection(bool)), action, SLOT(setEnabled(bool)));
Chris@211 608 menu->addAction(action);
Chris@211 609 m_rightButtonMenu->addAction(action);
Chris@189 610
Chris@187 611 m_showCandidatesAction = new QAction(tr("Show Pitch Candidates"), this);
Chris@187 612 m_showCandidatesAction->setShortcut(tr("Ctrl+Return"));
Chris@189 613 m_showCandidatesAction->setStatusTip(tr("Toggle the display of alternative pitch candidates for the selected region"));
Chris@189 614 m_keyReference->registerShortcut(m_showCandidatesAction);
Chris@187 615 connect(m_showCandidatesAction, SIGNAL(triggered()), this, SLOT(togglePitchCandidates()));
Chris@187 616 connect(this, SIGNAL(canClearSelection(bool)), m_showCandidatesAction, SLOT(setEnabled(bool)));
Chris@187 617 menu->addAction(m_showCandidatesAction);
Chris@189 618 m_rightButtonMenu->addAction(m_showCandidatesAction);
Chris@187 619
Chris@287 620 action = new QAction(tr("Remove Pitches"), this);
matthiasm@327 621 action->setShortcut(tr("Ctrl+Backspace"));
Chris@211 622 action->setStatusTip(tr("Remove all pitch estimates within the selected region, making it unvoiced"));
Chris@189 623 m_keyReference->registerShortcut(action);
Chris@187 624 connect(action, SIGNAL(triggered()), this, SLOT(clearPitches()));
Chris@167 625 connect(this, SIGNAL(canClearSelection(bool)), action, SLOT(setEnabled(bool)));
Chris@167 626 menu->addAction(action);
Chris@189 627 m_rightButtonMenu->addAction(action);
Chris@237 628
Chris@237 629 menu->addSeparator();
Chris@237 630 m_rightButtonMenu->addSeparator();
Chris@237 631
matthiasm@327 632 m_keyReference->setCategory(tr("Note Track"));
matthiasm@327 633
Chris@287 634 action = new QAction(tr("Split Note"), this);
matthiasm@327 635 action->setShortcut(tr("/"));
Chris@240 636 action->setStatusTip(tr("Split the note at the current playback position into two"));
Chris@237 637 m_keyReference->registerShortcut(action);
Chris@240 638 connect(action, SIGNAL(triggered()), this, SLOT(splitNote()));
Chris@240 639 connect(this, SIGNAL(canExportNotes(bool)), action, SLOT(setEnabled(bool)));
Chris@237 640 menu->addAction(action);
Chris@237 641 m_rightButtonMenu->addAction(action);
Chris@238 642
Chris@287 643 action = new QAction(tr("Merge Notes"), this);
matthiasm@327 644 action->setShortcut(tr("\\"));
Chris@238 645 action->setStatusTip(tr("Merge all notes within the selected region into a single note"));
Chris@238 646 m_keyReference->registerShortcut(action);
Chris@238 647 connect(action, SIGNAL(triggered()), this, SLOT(mergeNotes()));
Chris@238 648 connect(this, SIGNAL(canSnapNotes(bool)), action, SLOT(setEnabled(bool)));
Chris@238 649 menu->addAction(action);
Chris@238 650 m_rightButtonMenu->addAction(action);
matthiasm@292 651
matthiasm@292 652 action = new QAction(tr("Delete Notes"), this);
matthiasm@327 653 action->setShortcut(tr("Backspace"));
matthiasm@327 654 action->setStatusTip(tr("Delete all notes within the selected region"));
matthiasm@292 655 m_keyReference->registerShortcut(action);
matthiasm@292 656 connect(action, SIGNAL(triggered()), this, SLOT(deleteNotes()));
matthiasm@292 657 connect(this, SIGNAL(canSnapNotes(bool)), action, SLOT(setEnabled(bool)));
matthiasm@292 658 menu->addAction(action);
matthiasm@292 659 m_rightButtonMenu->addAction(action);
Chris@238 660
Chris@240 661 action = new QAction(tr("Form Note from Selection"), this);
matthiasm@327 662 action->setShortcut(tr("="));
Chris@240 663 action->setStatusTip(tr("Form a note spanning the selected region, splitting any existing notes at its boundaries"));
Chris@240 664 m_keyReference->registerShortcut(action);
Chris@240 665 connect(action, SIGNAL(triggered()), this, SLOT(formNoteFromSelection()));
Chris@240 666 connect(this, SIGNAL(canSnapNotes(bool)), action, SLOT(setEnabled(bool)));
Chris@240 667 menu->addAction(action);
Chris@240 668 m_rightButtonMenu->addAction(action);
Chris@247 669
Chris@238 670 action = new QAction(tr("Snap Notes to Pitch Track"), this);
Chris@238 671 action->setStatusTip(tr("Set notes within the selected region to the median frequency of their underlying pitches, or remove them if there are no underlying pitches"));
matthiasm@327 672 // m_keyReference->registerShortcut(action);
Chris@238 673 connect(action, SIGNAL(triggered()), this, SLOT(snapNotesToPitches()));
Chris@238 674 connect(this, SIGNAL(canSnapNotes(bool)), action, SLOT(setEnabled(bool)));
Chris@238 675 menu->addAction(action);
Chris@238 676 m_rightButtonMenu->addAction(action);
Chris@0 677 }
Chris@0 678
Chris@0 679 void
Chris@0 680 MainWindow::setupViewMenu()
Chris@0 681 {
Chris@0 682 if (m_mainMenusCreated) return;
Chris@0 683
Chris@0 684 IconLoader il;
Chris@0 685
Chris@0 686 QAction *action = 0;
Chris@0 687
Chris@0 688 m_keyReference->setCategory(tr("Panning and Navigation"));
Chris@0 689
Chris@0 690 QMenu *menu = menuBar()->addMenu(tr("&View"));
Chris@0 691 menu->setTearOffEnabled(true);
Chris@300 692 action = new QAction(tr("Peek &Left"), this);
Chris@300 693 action->setShortcut(tr("Alt+Left"));
Chris@300 694 action->setStatusTip(tr("Scroll the current pane to the left without changing the play position"));
Chris@0 695 connect(action, SIGNAL(triggered()), this, SLOT(scrollLeft()));
Chris@0 696 connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool)));
Chris@0 697 m_keyReference->registerShortcut(action);
Chris@0 698 menu->addAction(action);
gyorgyf@27 699
Chris@300 700 action = new QAction(tr("Peek &Right"), this);
Chris@300 701 action->setShortcut(tr("Alt+Right"));
Chris@300 702 action->setStatusTip(tr("Scroll the current pane to the right without changing the play position"));
Chris@0 703 connect(action, SIGNAL(triggered()), this, SLOT(scrollRight()));
Chris@0 704 connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool)));
Chris@0 705 m_keyReference->registerShortcut(action);
Chris@0 706 menu->addAction(action);
matthiasm@283 707
Chris@0 708 menu->addSeparator();
Chris@0 709
Chris@0 710 m_keyReference->setCategory(tr("Zoom"));
Chris@0 711
Chris@0 712 action = new QAction(il.load("zoom-in"),
Chris@0 713 tr("Zoom &In"), this);
Chris@0 714 action->setShortcut(tr("Up"));
Chris@0 715 action->setStatusTip(tr("Increase the zoom level"));
Chris@0 716 connect(action, SIGNAL(triggered()), this, SLOT(zoomIn()));
Chris@0 717 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
Chris@0 718 m_keyReference->registerShortcut(action);
Chris@0 719 menu->addAction(action);
gyorgyf@27 720
Chris@0 721 action = new QAction(il.load("zoom-out"),
Chris@0 722 tr("Zoom &Out"), this);
Chris@0 723 action->setShortcut(tr("Down"));
Chris@0 724 action->setStatusTip(tr("Decrease the zoom level"));
Chris@0 725 connect(action, SIGNAL(triggered()), this, SLOT(zoomOut()));
Chris@0 726 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
Chris@0 727 m_keyReference->registerShortcut(action);
Chris@0 728 menu->addAction(action);
gyorgyf@27 729
Chris@0 730 action = new QAction(tr("Restore &Default Zoom"), this);
Chris@0 731 action->setStatusTip(tr("Restore the zoom level to the default"));
Chris@0 732 connect(action, SIGNAL(triggered()), this, SLOT(zoomDefault()));
Chris@0 733 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
Chris@0 734 menu->addAction(action);
Chris@0 735
Chris@0 736 action = new QAction(il.load("zoom-fit"),
Chris@0 737 tr("Zoom to &Fit"), this);
Chris@0 738 action->setShortcut(tr("F"));
Chris@0 739 action->setStatusTip(tr("Zoom to show the whole file"));
Chris@0 740 connect(action, SIGNAL(triggered()), this, SLOT(zoomToFit()));
Chris@0 741 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
Chris@0 742 m_keyReference->registerShortcut(action);
Chris@0 743 menu->addAction(action);
Chris@227 744
Chris@227 745 menu->addSeparator();
Chris@227 746
Chris@227 747 action = new QAction(tr("Set Displayed Fre&quency Range..."), this);
Chris@227 748 action->setStatusTip(tr("Set the minimum and maximum frequencies in the visible display"));
Chris@227 749 connect(action, SIGNAL(triggered()), this, SLOT(editDisplayExtents()));
Chris@227 750 menu->addAction(action);
Chris@0 751 }
Chris@0 752
Chris@0 753 void
matthiasm@317 754 MainWindow::setupAnalysisMenu()
matthiasm@317 755 {
matthiasm@317 756 if (m_mainMenusCreated) return;
matthiasm@317 757
matthiasm@317 758 IconLoader il;
matthiasm@317 759
matthiasm@317 760 QAction *action = 0;
matthiasm@317 761
matthiasm@317 762 QMenu *menu = menuBar()->addMenu(tr("&Analysis"));
matthiasm@317 763 menu->setTearOffEnabled(true);
matthiasm@317 764
Chris@427 765 m_autoAnalyse = new QAction(tr("Auto-Analyse &New Audio"), this);
Chris@427 766 m_autoAnalyse->setStatusTip(tr("Automatically trigger analysis upon opening of a new audio file."));
Chris@427 767 m_autoAnalyse->setCheckable(true);
Chris@427 768 connect(m_autoAnalyse, SIGNAL(triggered()), this, SLOT(autoAnalysisToggled()));
Chris@427 769 menu->addAction(m_autoAnalyse);
Chris@427 770
Chris@427 771 action = new QAction(tr("&Analyse Now!"), this);
Chris@427 772 action->setStatusTip(tr("Trigger analysis of pitches and notes. (This will delete all existing pitches and notes.)"));
Chris@427 773 connect(action, SIGNAL(triggered()), this, SLOT(analyseNow()));
Chris@427 774 menu->addAction(action);
Chris@427 775 m_keyReference->registerShortcut(action);
Chris@427 776
Chris@427 777 menu->addSeparator();
Chris@427 778
Chris@427 779 m_precise = new QAction(tr("&Unbiased Timing (slow)"), this);
Chris@427 780 m_precise->setStatusTip(tr("Use a symmetric window in YIN to remove frequency-dependent timing bias. (This is slow!)"));
Chris@427 781 m_precise->setCheckable(true);
Chris@427 782 connect(m_precise, SIGNAL(triggered()), this, SLOT(precisionAnalysisToggled()));
Chris@427 783 menu->addAction(m_precise);
Chris@427 784
Chris@427 785 m_lowamp = new QAction(tr("&Penalise Soft Pitches"), this);
Chris@427 786 m_lowamp->setStatusTip(tr("Reduce the likelihood of detecting a pitch when the signal has low amplitude."));
Chris@427 787 m_lowamp->setCheckable(true);
Chris@427 788 connect(m_lowamp, SIGNAL(triggered()), this, SLOT(lowampAnalysisToggled()));
Chris@427 789 menu->addAction(m_lowamp);
Chris@427 790
Chris@427 791 m_onset = new QAction(tr("&High Onset Sensitivity"), this);
Chris@427 792 m_onset->setStatusTip(tr("Increase likelihood of separating notes, especially consecutive notes at the same pitch."));
Chris@427 793 m_onset->setCheckable(true);
Chris@427 794 connect(m_onset, SIGNAL(triggered()), this, SLOT(onsetAnalysisToggled()));
Chris@427 795 menu->addAction(m_onset);
Chris@427 796
Chris@427 797 m_prune = new QAction(tr("&Drop Short Notes"), this);
Chris@427 798 m_prune->setStatusTip(tr("Duration-based pruning: automatic note estimator will not output notes of less than 100ms duration."));
Chris@427 799 m_prune->setCheckable(true);
Chris@427 800 connect(m_prune, SIGNAL(triggered()), this, SLOT(pruneAnalysisToggled()));
Chris@427 801 menu->addAction(m_prune);
Chris@427 802
Chris@427 803 menu->addSeparator();
Chris@427 804
Chris@427 805 action = new QAction(tr("Reset Options to Defaults"), this);
Chris@427 806 action->setStatusTip(tr("Reset all of the Analyse menu options to their default settings."));
Chris@427 807 connect(action, SIGNAL(triggered()), this, SLOT(resetAnalyseOptions()));
Chris@427 808 menu->addAction(action);
Chris@427 809
Chris@427 810 updateAnalyseStates();
Chris@427 811 }
Chris@427 812
Chris@427 813 void
Chris@427 814 MainWindow::resetAnalyseOptions()
Chris@427 815 {
Chris@427 816 QSettings settings;
Chris@427 817 settings.beginGroup("Analyser");
Chris@673 818
Chris@427 819 settings.setValue("auto-analysis", true);
Chris@673 820
Chris@673 821 auto keyMap = Analyser::getAnalysisSettings();
Chris@673 822 for (auto p: keyMap) {
Chris@673 823 settings.setValue(p.first, p.second);
Chris@673 824 }
Chris@673 825
Chris@427 826 settings.endGroup();
Chris@427 827 updateAnalyseStates();
Chris@427 828 }
Chris@427 829
Chris@427 830 void
Chris@427 831 MainWindow::updateAnalyseStates()
Chris@427 832 {
Chris@323 833 QSettings settings;
Chris@323 834 settings.beginGroup("Analyser");
Chris@673 835
Chris@323 836 bool autoAnalyse = settings.value("auto-analysis", true).toBool();
Chris@673 837 m_autoAnalyse->setChecked(autoAnalyse);
Chris@673 838
Chris@673 839 std::map<QString, QAction *> actions {
Chris@673 840 { "precision-analysis", m_precise },
Chris@673 841 { "lowamp-analysis", m_lowamp },
Chris@673 842 { "onset-analysis", m_onset },
Chris@673 843 { "prune-analysis", m_prune }
Chris@673 844 };
Chris@673 845
Chris@673 846 auto keyMap = Analyser::getAnalysisSettings();
Chris@673 847
Chris@673 848 for (auto p: actions) {
Chris@673 849 auto ki = keyMap.find(p.first);
Chris@673 850 if (ki != keyMap.end()) {
Chris@673 851 p.second->setChecked(settings.value
Chris@673 852 (ki->first, ki->second).toBool());
Chris@673 853 } else {
Chris@673 854 throw std::logic_error("Internal error: One or more analysis settings keys not found in map returned by Analyser: check updateAnalyseStates and getAnalysisSettings");
Chris@673 855 }
Chris@673 856 }
Chris@673 857
Chris@323 858 settings.endGroup();
Chris@323 859 }
Chris@323 860
Chris@323 861 void
Chris@323 862 MainWindow::autoAnalysisToggled()
Chris@323 863 {
Chris@323 864 QAction *a = qobject_cast<QAction *>(sender());
Chris@323 865 if (!a) return;
Chris@323 866
Chris@323 867 bool set = a->isChecked();
Chris@323 868
Chris@323 869 QSettings settings;
Chris@323 870 settings.beginGroup("Analyser");
Chris@323 871 settings.setValue("auto-analysis", set);
Chris@323 872 settings.endGroup();
Chris@673 873
Chris@673 874 // make result visible explicitly, in case e.g. we just set the wrong key
Chris@673 875 updateAnalyseStates();
Chris@323 876 }
Chris@323 877
Chris@323 878 void
Chris@323 879 MainWindow::precisionAnalysisToggled()
Chris@323 880 {
Chris@323 881 QAction *a = qobject_cast<QAction *>(sender());
Chris@323 882 if (!a) return;
Chris@323 883
Chris@323 884 bool set = a->isChecked();
Chris@323 885
Chris@323 886 QSettings settings;
Chris@323 887 settings.beginGroup("Analyser");
Chris@323 888 settings.setValue("precision-analysis", set);
Chris@323 889 settings.endGroup();
Chris@323 890
Chris@326 891 // don't run analyseNow() automatically -- it's a destructive operation
Chris@673 892
Chris@673 893 // make result visible explicitly, in case e.g. we just set the wrong key
Chris@673 894 updateAnalyseStates();
matthiasm@317 895 }
matthiasm@317 896
matthiasm@317 897 void
matthiasm@345 898 MainWindow::lowampAnalysisToggled()
matthiasm@345 899 {
matthiasm@345 900 QAction *a = qobject_cast<QAction *>(sender());
matthiasm@345 901 if (!a) return;
matthiasm@345 902
matthiasm@345 903 bool set = a->isChecked();
matthiasm@345 904
matthiasm@345 905 QSettings settings;
matthiasm@345 906 settings.beginGroup("Analyser");
matthiasm@345 907 settings.setValue("lowamp-analysis", set);
matthiasm@345 908 settings.endGroup();
matthiasm@345 909
matthiasm@345 910 // don't run analyseNow() automatically -- it's a destructive operation
Chris@673 911
Chris@673 912 // make result visible explicitly, in case e.g. we just set the wrong key
Chris@673 913 updateAnalyseStates();
matthiasm@345 914 }
matthiasm@345 915
matthiasm@345 916 void
matthiasm@423 917 MainWindow::onsetAnalysisToggled()
matthiasm@423 918 {
matthiasm@423 919 QAction *a = qobject_cast<QAction *>(sender());
matthiasm@423 920 if (!a) return;
matthiasm@423 921
matthiasm@423 922 bool set = a->isChecked();
matthiasm@423 923
matthiasm@423 924 QSettings settings;
matthiasm@423 925 settings.beginGroup("Analyser");
matthiasm@423 926 settings.setValue("onset-analysis", set);
matthiasm@423 927 settings.endGroup();
matthiasm@423 928
matthiasm@423 929 // don't run analyseNow() automatically -- it's a destructive operation
Chris@673 930
Chris@673 931 // make result visible explicitly, in case e.g. we just set the wrong key
Chris@673 932 updateAnalyseStates();
matthiasm@423 933 }
matthiasm@423 934
matthiasm@423 935 void
matthiasm@423 936 MainWindow::pruneAnalysisToggled()
matthiasm@423 937 {
matthiasm@423 938 QAction *a = qobject_cast<QAction *>(sender());
matthiasm@423 939 if (!a) return;
matthiasm@423 940
matthiasm@423 941 bool set = a->isChecked();
matthiasm@423 942
matthiasm@423 943 QSettings settings;
matthiasm@423 944 settings.beginGroup("Analyser");
matthiasm@423 945 settings.setValue("prune-analysis", set);
matthiasm@423 946 settings.endGroup();
matthiasm@423 947
matthiasm@423 948 // don't run analyseNow() automatically -- it's a destructive operation
Chris@673 949
Chris@673 950 // make result visible explicitly, in case e.g. we just set the wrong key
Chris@673 951 updateAnalyseStates();
matthiasm@423 952 }
matthiasm@423 953
matthiasm@423 954 void
Chris@0 955 MainWindow::setupHelpMenu()
Chris@0 956 {
Chris@0 957 QMenu *menu = menuBar()->addMenu(tr("&Help"));
Chris@0 958 menu->setTearOffEnabled(true);
Chris@0 959
Chris@0 960 m_keyReference->setCategory(tr("Help"));
Chris@0 961
Chris@0 962 IconLoader il;
Chris@0 963
Chris@1 964 QString name = QApplication::applicationName();
matthiasm@367 965 QAction *action;
matthiasm@367 966
Chris@585 967 action = new QAction(il.load("help"),
Chris@585 968 tr("&Help Reference"), this);
Chris@585 969 action->setShortcut(tr("F1"));
Chris@585 970 action->setStatusTip(tr("Open the %1 reference manual").arg(name));
Chris@585 971 connect(action, SIGNAL(triggered()), this, SLOT(help()));
Chris@585 972 m_keyReference->registerShortcut(action);
Chris@585 973 menu->addAction(action);
Chris@585 974
Chris@0 975 action = new QAction(tr("&Key and Mouse Reference"), this);
Chris@0 976 action->setShortcut(tr("F2"));
Chris@1 977 action->setStatusTip(tr("Open a window showing the keystrokes you can use in %1").arg(name));
Chris@0 978 connect(action, SIGNAL(triggered()), this, SLOT(keyReference()));
Chris@0 979 m_keyReference->registerShortcut(action);
Chris@0 980 menu->addAction(action);
Chris@0 981
Chris@585 982 action = new QAction(tr("What's &New In This Release?"), this);
Chris@585 983 action->setStatusTip(tr("List the changes in this release (and every previous release) of %1").arg(name));
Chris@585 984 connect(action, SIGNAL(triggered()), this, SLOT(whatsNew()));
Chris@0 985 menu->addAction(action);
Chris@0 986
Chris@1 987 action = new QAction(tr("&About %1").arg(name), this);
Chris@1 988 action->setStatusTip(tr("Show information about %1").arg(name));
Chris@0 989 connect(action, SIGNAL(triggered()), this, SLOT(about()));
Chris@0 990 menu->addAction(action);
Chris@0 991 }
Chris@0 992
Chris@0 993 void
Chris@0 994 MainWindow::setupRecentFilesMenu()
Chris@0 995 {
Chris@0 996 m_recentFilesMenu->clear();
Chris@0 997 vector<QString> files = m_recentFiles.getRecent();
Chris@0 998 for (size_t i = 0; i < files.size(); ++i) {
Chris@50 999 QAction *action = new QAction(files[i], this);
Chris@50 1000 connect(action, SIGNAL(triggered()), this, SLOT(openRecentFile()));
Chris@0 1001 if (i == 0) {
Chris@0 1002 action->setShortcut(tr("Ctrl+R"));
Chris@0 1003 m_keyReference->registerShortcut
Chris@0 1004 (tr("Re-open"),
Chris@50 1005 action->shortcut().toString(),
Chris@0 1006 tr("Re-open the current or most recently opened file"));
Chris@0 1007 }
Chris@50 1008 m_recentFilesMenu->addAction(action);
Chris@0 1009 }
Chris@0 1010 }
Chris@0 1011
Chris@0 1012 void
Chris@0 1013 MainWindow::setupToolbars()
Chris@0 1014 {
Chris@0 1015 m_keyReference->setCategory(tr("Playback and Transport Controls"));
Chris@0 1016
Chris@0 1017 IconLoader il;
Chris@0 1018
Chris@0 1019 QMenu *menu = m_playbackMenu = menuBar()->addMenu(tr("Play&back"));
Chris@0 1020 menu->setTearOffEnabled(true);
Chris@0 1021 m_rightButtonMenu->addSeparator();
Chris@0 1022 m_rightButtonPlaybackMenu = m_rightButtonMenu->addMenu(tr("Playback"));
Chris@0 1023
Chris@0 1024 QToolBar *toolbar = addToolBar(tr("Playback Toolbar"));
Chris@0 1025
Chris@0 1026 QAction *rwdStartAction = toolbar->addAction(il.load("rewind-start"),
Chris@0 1027 tr("Rewind to Start"));
Chris@0 1028 rwdStartAction->setShortcut(tr("Home"));
Chris@0 1029 rwdStartAction->setStatusTip(tr("Rewind to the start"));
Chris@0 1030 connect(rwdStartAction, SIGNAL(triggered()), this, SLOT(rewindStart()));
Chris@0 1031 connect(this, SIGNAL(canPlay(bool)), rwdStartAction, SLOT(setEnabled(bool)));
Chris@0 1032
Chris@0 1033 QAction *m_rwdAction = toolbar->addAction(il.load("rewind"),
Chris@0 1034 tr("Rewind"));
Chris@300 1035 m_rwdAction->setShortcut(tr("Left"));
Chris@300 1036 m_rwdAction->setStatusTip(tr("Rewind to the previous one-second boundary"));
Chris@0 1037 connect(m_rwdAction, SIGNAL(triggered()), this, SLOT(rewind()));
Chris@0 1038 connect(this, SIGNAL(canRewind(bool)), m_rwdAction, SLOT(setEnabled(bool)));
Chris@0 1039
Chris@300 1040 setDefaultFfwdRwdStep(RealTime(1, 0));
Chris@300 1041
Chris@0 1042 QAction *playAction = toolbar->addAction(il.load("playpause"),
Chris@0 1043 tr("Play / Pause"));
Chris@0 1044 playAction->setCheckable(true);
Chris@0 1045 playAction->setShortcut(tr("Space"));
Chris@0 1046 playAction->setStatusTip(tr("Start or stop playback from the current position"));
Chris@0 1047 connect(playAction, SIGNAL(triggered()), this, SLOT(play()));
Chris@0 1048 connect(m_playSource, SIGNAL(playStatusChanged(bool)),
gyorgyf@27 1049 playAction, SLOT(setChecked(bool)));
Chris@0 1050 connect(this, SIGNAL(canPlay(bool)), playAction, SLOT(setEnabled(bool)));
Chris@0 1051
Chris@0 1052 m_ffwdAction = toolbar->addAction(il.load("ffwd"),
Chris@0 1053 tr("Fast Forward"));
Chris@300 1054 m_ffwdAction->setShortcut(tr("Right"));
Chris@300 1055 m_ffwdAction->setStatusTip(tr("Fast-forward to the next one-second boundary"));
Chris@0 1056 connect(m_ffwdAction, SIGNAL(triggered()), this, SLOT(ffwd()));
Chris@0 1057 connect(this, SIGNAL(canFfwd(bool)), m_ffwdAction, SLOT(setEnabled(bool)));
Chris@0 1058
Chris@0 1059 QAction *ffwdEndAction = toolbar->addAction(il.load("ffwd-end"),
Chris@0 1060 tr("Fast Forward to End"));
Chris@0 1061 ffwdEndAction->setShortcut(tr("End"));
Chris@0 1062 ffwdEndAction->setStatusTip(tr("Fast-forward to the end"));
Chris@0 1063 connect(ffwdEndAction, SIGNAL(triggered()), this, SLOT(ffwdEnd()));
Chris@0 1064 connect(this, SIGNAL(canPlay(bool)), ffwdEndAction, SLOT(setEnabled(bool)));
Chris@6 1065
Chris@476 1066 QAction *recordAction = toolbar->addAction(il.load("record"),
Chris@476 1067 tr("Record"));
Chris@476 1068 recordAction->setCheckable(true);
Chris@476 1069 recordAction->setShortcut(tr("Ctrl+Space"));
Chris@476 1070 recordAction->setStatusTip(tr("Record a new audio file"));
Chris@476 1071 connect(recordAction, SIGNAL(triggered()), this, SLOT(record()));
Chris@476 1072 connect(m_recordTarget, SIGNAL(recordStatusChanged(bool)),
Chris@476 1073 recordAction, SLOT(setChecked(bool)));
Chris@497 1074 connect(m_recordTarget, SIGNAL(recordCompleted()),
Chris@497 1075 this, SLOT(analyseNow()));
Chris@476 1076 connect(this, SIGNAL(canRecord(bool)),
Chris@476 1077 recordAction, SLOT(setEnabled(bool)));
Chris@476 1078
Chris@0 1079 toolbar = addToolBar(tr("Play Mode Toolbar"));
Chris@0 1080
Chris@0 1081 QAction *psAction = toolbar->addAction(il.load("playselection"),
Chris@0 1082 tr("Constrain Playback to Selection"));
Chris@0 1083 psAction->setCheckable(true);
Chris@0 1084 psAction->setChecked(m_viewManager->getPlaySelectionMode());
Chris@0 1085 psAction->setShortcut(tr("s"));
Chris@0 1086 psAction->setStatusTip(tr("Constrain playback to the selected regions"));
Chris@0 1087 connect(m_viewManager, SIGNAL(playSelectionModeChanged(bool)),
Chris@0 1088 psAction, SLOT(setChecked(bool)));
Chris@0 1089 connect(psAction, SIGNAL(triggered()), this, SLOT(playSelectionToggled()));
Chris@0 1090 connect(this, SIGNAL(canPlaySelection(bool)), psAction, SLOT(setEnabled(bool)));
Chris@0 1091
Chris@0 1092 QAction *plAction = toolbar->addAction(il.load("playloop"),
Chris@0 1093 tr("Loop Playback"));
Chris@0 1094 plAction->setCheckable(true);
Chris@0 1095 plAction->setChecked(m_viewManager->getPlayLoopMode());
Chris@0 1096 plAction->setShortcut(tr("l"));
Chris@0 1097 plAction->setStatusTip(tr("Loop playback"));
Chris@0 1098 connect(m_viewManager, SIGNAL(playLoopModeChanged(bool)),
Chris@0 1099 plAction, SLOT(setChecked(bool)));
Chris@0 1100 connect(plAction, SIGNAL(triggered()), this, SLOT(playLoopToggled()));
Chris@0 1101 connect(this, SIGNAL(canPlay(bool)), plAction, SLOT(setEnabled(bool)));
Chris@0 1102
Chris@300 1103 QAction *oneLeftAction = new QAction(tr("&One Note Left"), this);
Chris@300 1104 oneLeftAction->setShortcut(tr("Ctrl+Left"));
Chris@300 1105 oneLeftAction->setStatusTip(tr("Move cursor to the preceding note (or silence) onset."));
Chris@300 1106 connect(oneLeftAction, SIGNAL(triggered()), this, SLOT(moveOneNoteLeft()));
Chris@300 1107 connect(this, SIGNAL(canScroll(bool)), oneLeftAction, SLOT(setEnabled(bool)));
Chris@300 1108
Chris@300 1109 QAction *oneRightAction = new QAction(tr("O&ne Note Right"), this);
Chris@300 1110 oneRightAction->setShortcut(tr("Ctrl+Right"));
Chris@300 1111 oneRightAction->setStatusTip(tr("Move cursor to the succeeding note (or silence)."));
Chris@300 1112 connect(oneRightAction, SIGNAL(triggered()), this, SLOT(moveOneNoteRight()));
Chris@300 1113 connect(this, SIGNAL(canScroll(bool)), oneRightAction, SLOT(setEnabled(bool)));
Chris@300 1114
Chris@300 1115 QAction *selectOneLeftAction = new QAction(tr("&Select One Note Left"), this);
Chris@300 1116 selectOneLeftAction->setShortcut(tr("Ctrl+Shift+Left"));
Chris@300 1117 selectOneLeftAction->setStatusTip(tr("Select to the preceding note (or silence) onset."));
Chris@300 1118 connect(selectOneLeftAction, SIGNAL(triggered()), this, SLOT(selectOneNoteLeft()));
Chris@300 1119 connect(this, SIGNAL(canScroll(bool)), selectOneLeftAction, SLOT(setEnabled(bool)));
Chris@300 1120
Chris@300 1121 QAction *selectOneRightAction = new QAction(tr("S&elect One Note Right"), this);
Chris@300 1122 selectOneRightAction->setShortcut(tr("Ctrl+Shift+Right"));
Chris@300 1123 selectOneRightAction->setStatusTip(tr("Select to the succeeding note (or silence)."));
Chris@300 1124 connect(selectOneRightAction, SIGNAL(triggered()), this, SLOT(selectOneNoteRight()));
Chris@300 1125 connect(this, SIGNAL(canScroll(bool)), selectOneRightAction, SLOT(setEnabled(bool)));
Chris@300 1126
Chris@0 1127 m_keyReference->registerShortcut(psAction);
Chris@0 1128 m_keyReference->registerShortcut(plAction);
Chris@0 1129 m_keyReference->registerShortcut(playAction);
Chris@476 1130 m_keyReference->registerShortcut(recordAction);
Chris@0 1131 m_keyReference->registerShortcut(m_rwdAction);
Chris@0 1132 m_keyReference->registerShortcut(m_ffwdAction);
Chris@0 1133 m_keyReference->registerShortcut(rwdStartAction);
Chris@0 1134 m_keyReference->registerShortcut(ffwdEndAction);
Chris@476 1135 m_keyReference->registerShortcut(recordAction);
Chris@300 1136 m_keyReference->registerShortcut(oneLeftAction);
Chris@300 1137 m_keyReference->registerShortcut(oneRightAction);
Chris@300 1138 m_keyReference->registerShortcut(selectOneLeftAction);
Chris@300 1139 m_keyReference->registerShortcut(selectOneRightAction);
Chris@0 1140
Chris@6 1141 menu->addAction(playAction);
Chris@0 1142 menu->addAction(psAction);
Chris@0 1143 menu->addAction(plAction);
Chris@0 1144 menu->addSeparator();
Chris@0 1145 menu->addAction(m_rwdAction);
Chris@0 1146 menu->addAction(m_ffwdAction);
Chris@0 1147 menu->addSeparator();
Chris@0 1148 menu->addAction(rwdStartAction);
Chris@0 1149 menu->addAction(ffwdEndAction);
Chris@0 1150 menu->addSeparator();
Chris@300 1151 menu->addAction(oneLeftAction);
Chris@300 1152 menu->addAction(oneRightAction);
Chris@300 1153 menu->addAction(selectOneLeftAction);
Chris@300 1154 menu->addAction(selectOneRightAction);
Chris@300 1155 menu->addSeparator();
Chris@476 1156 menu->addAction(recordAction);
Chris@476 1157 menu->addSeparator();
Chris@0 1158
Chris@0 1159 m_rightButtonPlaybackMenu->addAction(playAction);
Chris@0 1160 m_rightButtonPlaybackMenu->addAction(psAction);
Chris@0 1161 m_rightButtonPlaybackMenu->addAction(plAction);
Chris@0 1162 m_rightButtonPlaybackMenu->addSeparator();
Chris@0 1163 m_rightButtonPlaybackMenu->addAction(m_rwdAction);
Chris@0 1164 m_rightButtonPlaybackMenu->addAction(m_ffwdAction);
Chris@0 1165 m_rightButtonPlaybackMenu->addSeparator();
Chris@0 1166 m_rightButtonPlaybackMenu->addAction(rwdStartAction);
Chris@0 1167 m_rightButtonPlaybackMenu->addAction(ffwdEndAction);
Chris@0 1168 m_rightButtonPlaybackMenu->addSeparator();
Chris@300 1169 m_rightButtonPlaybackMenu->addAction(oneLeftAction);
Chris@300 1170 m_rightButtonPlaybackMenu->addAction(oneRightAction);
Chris@300 1171 m_rightButtonPlaybackMenu->addAction(selectOneLeftAction);
Chris@300 1172 m_rightButtonPlaybackMenu->addAction(selectOneRightAction);
Chris@300 1173 m_rightButtonPlaybackMenu->addSeparator();
Chris@476 1174 m_rightButtonPlaybackMenu->addAction(recordAction);
Chris@476 1175 m_rightButtonPlaybackMenu->addSeparator();
Chris@0 1176
Chris@0 1177 QAction *fastAction = menu->addAction(tr("Speed Up"));
Chris@0 1178 fastAction->setShortcut(tr("Ctrl+PgUp"));
Chris@0 1179 fastAction->setStatusTip(tr("Time-stretch playback to speed it up without changing pitch"));
Chris@0 1180 connect(fastAction, SIGNAL(triggered()), this, SLOT(speedUpPlayback()));
Chris@0 1181 connect(this, SIGNAL(canSpeedUpPlayback(bool)), fastAction, SLOT(setEnabled(bool)));
Chris@0 1182
Chris@0 1183 QAction *slowAction = menu->addAction(tr("Slow Down"));
Chris@0 1184 slowAction->setShortcut(tr("Ctrl+PgDown"));
Chris@0 1185 slowAction->setStatusTip(tr("Time-stretch playback to slow it down without changing pitch"));
Chris@0 1186 connect(slowAction, SIGNAL(triggered()), this, SLOT(slowDownPlayback()));
Chris@0 1187 connect(this, SIGNAL(canSlowDownPlayback(bool)), slowAction, SLOT(setEnabled(bool)));
Chris@0 1188
Chris@0 1189 QAction *normalAction = menu->addAction(tr("Restore Normal Speed"));
Chris@0 1190 normalAction->setShortcut(tr("Ctrl+Home"));
Chris@0 1191 normalAction->setStatusTip(tr("Restore non-time-stretched playback"));
Chris@0 1192 connect(normalAction, SIGNAL(triggered()), this, SLOT(restoreNormalPlayback()));
Chris@0 1193 connect(this, SIGNAL(canChangePlaybackSpeed(bool)), normalAction, SLOT(setEnabled(bool)));
Chris@0 1194
Chris@0 1195 m_keyReference->registerShortcut(fastAction);
Chris@0 1196 m_keyReference->registerShortcut(slowAction);
Chris@0 1197 m_keyReference->registerShortcut(normalAction);
Chris@0 1198
Chris@0 1199 m_rightButtonPlaybackMenu->addAction(fastAction);
Chris@0 1200 m_rightButtonPlaybackMenu->addAction(slowAction);
Chris@0 1201 m_rightButtonPlaybackMenu->addAction(normalAction);
Chris@6 1202
Chris@195 1203 toolbar = new QToolBar(tr("Playback Controls"));
Chris@195 1204 addToolBar(Qt::BottomToolBarArea, toolbar);
Chris@195 1205
Chris@6 1206 toolbar->addWidget(m_playSpeed);
Chris@6 1207 toolbar->addWidget(m_fader);
Chris@0 1208
Chris@128 1209 toolbar = addToolBar(tr("Show and Play"));
matthiasm@258 1210 addToolBar(Qt::BottomToolBarArea, toolbar);
Chris@144 1211
Chris@144 1212 m_showAudio = toolbar->addAction(il.load("waveform"), tr("Show Audio"));
Chris@144 1213 m_showAudio->setCheckable(true);
Chris@144 1214 connect(m_showAudio, SIGNAL(triggered()), this, SLOT(showAudioToggled()));
Chris@144 1215 connect(this, SIGNAL(canPlay(bool)), m_showAudio, SLOT(setEnabled(bool)));
Chris@144 1216
Chris@424 1217 m_playAudio = toolbar->addAction(il.load("speaker"), tr("Play Audio"));
Chris@424 1218 m_playAudio->setCheckable(true);
Chris@424 1219 connect(m_playAudio, SIGNAL(triggered()), this, SLOT(playAudioToggled()));
Chris@424 1220 connect(this, SIGNAL(canPlayWaveform(bool)), m_playAudio, SLOT(setEnabled(bool)));
Chris@424 1221
Chris@440 1222 int lpwSize, bigLpwSize;
Chris@440 1223 #ifdef Q_OS_MAC
Chris@440 1224 lpwSize = m_viewManager->scalePixelSize(32); // Mac toolbars are fatter
Chris@442 1225 bigLpwSize = int(lpwSize * 2.2);
Chris@440 1226 #else
Chris@440 1227 lpwSize = m_viewManager->scalePixelSize(26);
Chris@442 1228 bigLpwSize = int(lpwSize * 2.8);
Chris@440 1229 #endif
Chris@440 1230
Chris@440 1231 m_audioLPW->setImageSize(lpwSize);
Chris@440 1232 m_audioLPW->setBigImageSize(bigLpwSize);
Chris@414 1233 toolbar->addWidget(m_audioLPW);
Chris@414 1234
Chris@290 1235 // Pitch (f0)
Chris@290 1236 QLabel *spacer = new QLabel; // blank
Chris@419 1237 spacer->setFixedWidth(m_viewManager->scalePixelSize(30));
Chris@290 1238 toolbar->addWidget(spacer);
Chris@145 1239
Chris@144 1240 m_showPitch = toolbar->addAction(il.load("values"), tr("Show Pitch Track"));
Chris@144 1241 m_showPitch->setCheckable(true);
Chris@144 1242 connect(m_showPitch, SIGNAL(triggered()), this, SLOT(showPitchToggled()));
Chris@144 1243 connect(this, SIGNAL(canPlay(bool)), m_showPitch, SLOT(setEnabled(bool)));
Chris@144 1244
Chris@405 1245 if (m_withSonification) {
Chris@424 1246 m_playPitch = toolbar->addAction(il.load("speaker"), tr("Play Pitch Track"));
Chris@424 1247 m_playPitch->setCheckable(true);
Chris@424 1248 connect(m_playPitch, SIGNAL(triggered()), this, SLOT(playPitchToggled()));
Chris@424 1249 connect(this, SIGNAL(canPlayPitch(bool)), m_playPitch, SLOT(setEnabled(bool)));
Chris@424 1250
Chris@440 1251 m_pitchLPW->setImageSize(lpwSize);
Chris@440 1252 m_pitchLPW->setBigImageSize(bigLpwSize);
Chris@404 1253 toolbar->addWidget(m_pitchLPW);
Chris@424 1254 } else {
Chris@424 1255 m_playPitch = 0;
matthiasm@366 1256 }
Chris@404 1257
Chris@290 1258 // Notes
Chris@290 1259 spacer = new QLabel;
Chris@419 1260 spacer->setFixedWidth(m_viewManager->scalePixelSize(30));
Chris@290 1261 toolbar->addWidget(spacer);
Chris@290 1262
Chris@144 1263 m_showNotes = toolbar->addAction(il.load("notes"), tr("Show Notes"));
Chris@144 1264 m_showNotes->setCheckable(true);
Chris@144 1265 connect(m_showNotes, SIGNAL(triggered()), this, SLOT(showNotesToggled()));
Chris@144 1266 connect(this, SIGNAL(canPlay(bool)), m_showNotes, SLOT(setEnabled(bool)));
justin@156 1267
Chris@405 1268 if (m_withSonification) {
Chris@424 1269 m_playNotes = toolbar->addAction(il.load("speaker"), tr("Play Notes"));
Chris@424 1270 m_playNotes->setCheckable(true);
Chris@424 1271 connect(m_playNotes, SIGNAL(triggered()), this, SLOT(playNotesToggled()));
Chris@424 1272 connect(this, SIGNAL(canPlayNotes(bool)), m_playNotes, SLOT(setEnabled(bool)));
Chris@424 1273
Chris@440 1274 m_notesLPW->setImageSize(lpwSize);
Chris@440 1275 m_notesLPW->setBigImageSize(bigLpwSize);
Chris@404 1276 toolbar->addWidget(m_notesLPW);
Chris@424 1277 } else {
Chris@424 1278 m_playNotes = 0;
matthiasm@366 1279 }
justin@156 1280
justin@156 1281 // Spectrogram
Chris@290 1282 spacer = new QLabel;
Chris@419 1283 spacer->setFixedWidth(m_viewManager->scalePixelSize(30));
Chris@290 1284 toolbar->addWidget(spacer);
Chris@290 1285
matthiasm@366 1286 if (!m_withSpectrogram)
matthiasm@366 1287 {
matthiasm@366 1288 m_showSpect = new QAction(tr("Show Spectrogram"), this);
matthiasm@366 1289 } else {
matthiasm@366 1290 m_showSpect = toolbar->addAction(il.load("spectrogram"), tr("Show Spectrogram"));
matthiasm@366 1291 }
justin@156 1292 m_showSpect->setCheckable(true);
justin@156 1293 connect(m_showSpect, SIGNAL(triggered()), this, SLOT(showSpectToggled()));
justin@156 1294 connect(this, SIGNAL(canPlay(bool)), m_showSpect, SLOT(setEnabled(bool)));
Chris@128 1295
Chris@0 1296 Pane::registerShortcuts(*m_keyReference);
matthiasm@366 1297
Chris@405 1298 updateLayerStatuses();
Chris@609 1299
Chris@657 1300 // QTimer::singleShot(500, this, SLOT(betaReleaseWarning()));
Chris@0 1301 }
Chris@0 1302
matthiasm@281 1303
matthiasm@281 1304 void
matthiasm@281 1305 MainWindow::moveOneNoteRight()
matthiasm@281 1306 {
matthiasm@281 1307 // cerr << "MainWindow::moveOneNoteRight" << endl;
matthiasm@296 1308 moveByOneNote(true, false);
matthiasm@281 1309 }
matthiasm@281 1310
matthiasm@281 1311 void
matthiasm@281 1312 MainWindow::moveOneNoteLeft()
matthiasm@281 1313 {
matthiasm@281 1314 // cerr << "MainWindow::moveOneNoteLeft" << endl;
matthiasm@296 1315 moveByOneNote(false, false);
matthiasm@281 1316 }
matthiasm@281 1317
matthiasm@281 1318 void
matthiasm@283 1319 MainWindow::selectOneNoteRight()
matthiasm@283 1320 {
matthiasm@296 1321 moveByOneNote(true, true);
matthiasm@283 1322 }
matthiasm@283 1323
matthiasm@283 1324 void
matthiasm@283 1325 MainWindow::selectOneNoteLeft()
matthiasm@283 1326 {
matthiasm@296 1327 moveByOneNote(false, true);
matthiasm@283 1328 }
matthiasm@283 1329
matthiasm@283 1330
matthiasm@283 1331 void
matthiasm@296 1332 MainWindow::moveByOneNote(bool right, bool doSelect)
matthiasm@281 1333 {
Chris@399 1334 sv_frame_t frame = m_viewManager->getPlaybackFrame();
matthiasm@301 1335 cerr << "MainWindow::moveByOneNote startframe: " << frame << endl;
matthiasm@281 1336
matthiasm@304 1337 bool isAtSelectionBoundary = false;
matthiasm@304 1338 MultiSelection::SelectionList selections = m_viewManager->getSelections();
matthiasm@304 1339 if (!selections.empty()) {
matthiasm@304 1340 Selection sel = *selections.begin();
matthiasm@342 1341 isAtSelectionBoundary = (frame == sel.getStartFrame()) || (frame == sel.getEndFrame());
matthiasm@304 1342 }
matthiasm@304 1343 if (!doSelect || !isAtSelectionBoundary) {
matthiasm@296 1344 m_selectionAnchor = frame;
matthiasm@296 1345 }
matthiasm@296 1346
matthiasm@281 1347 Layer *layer = m_analyser->getLayer(Analyser::Notes);
matthiasm@281 1348 if (!layer) return;
matthiasm@281 1349
Chris@573 1350 auto model = ModelById::getAs<NoteModel>(layer->getModel());
matthiasm@281 1351 if (!model) return;
matthiasm@281 1352
Chris@570 1353 //!!! This seems like a strange and inefficient way to do this -
Chris@570 1354 //!!! there is almost certainly a better way making use of
Chris@570 1355 //!!! EventSeries api
Chris@570 1356
Chris@570 1357 EventVector points = model->getAllEvents();
matthiasm@281 1358 if (points.empty()) return;
matthiasm@281 1359
Chris@570 1360 EventVector::iterator i = points.begin();
Chris@399 1361 std::set<sv_frame_t> snapFrames;
matthiasm@281 1362 snapFrames.insert(0);
matthiasm@281 1363 while (i != points.end()) {
Chris@570 1364 snapFrames.insert(i->getFrame());
Chris@570 1365 snapFrames.insert(i->getFrame() + i->getDuration() + 1);
matthiasm@281 1366 ++i;
matthiasm@281 1367 }
Chris@399 1368 std::set<sv_frame_t>::iterator i2;
Chris@399 1369 if (snapFrames.find(frame) == snapFrames.end()) {
matthiasm@281 1370 // we're not on an existing snap point, so go to previous
matthiasm@281 1371 snapFrames.insert(frame);
matthiasm@281 1372 }
matthiasm@281 1373 i2 = snapFrames.find(frame);
Chris@399 1374 if (right) {
matthiasm@281 1375 i2++;
matthiasm@281 1376 if (i2 == snapFrames.end()) i2--;
matthiasm@281 1377 } else {
matthiasm@281 1378 if (i2 != snapFrames.begin()) i2--;
matthiasm@281 1379 }
matthiasm@281 1380 frame = *i2;
matthiasm@281 1381 m_viewManager->setPlaybackFrame(frame);
matthiasm@296 1382 if (doSelect) {
matthiasm@296 1383 Selection sel;
matthiasm@296 1384 if (frame > m_selectionAnchor) {
matthiasm@342 1385 sel = Selection(m_selectionAnchor, frame);
matthiasm@296 1386 } else {
matthiasm@342 1387 sel = Selection(frame, m_selectionAnchor);
matthiasm@296 1388 }
matthiasm@296 1389 m_viewManager->setSelection(sel);
matthiasm@296 1390 }
matthiasm@301 1391 cerr << "MainWindow::moveByOneNote endframe: " << frame << endl;
matthiasm@281 1392 }
matthiasm@281 1393
Chris@0 1394 void
Chris@70 1395 MainWindow::toolNavigateSelected()
Chris@70 1396 {
Chris@70 1397 m_viewManager->setToolMode(ViewManager::NavigateMode);
Chris@70 1398 m_intelligentActionOn = true;
Chris@70 1399 }
Chris@70 1400
Chris@70 1401 void
Chris@70 1402 MainWindow::toolEditSelected()
Chris@70 1403 {
matthiasm@294 1404 cerr << "MainWindow::toolEditSelected" << endl;
Chris@77 1405 m_viewManager->setToolMode(ViewManager::NoteEditMode);
Chris@70 1406 m_intelligentActionOn = true;
Chris@70 1407 m_analyser->setIntelligentActions(m_intelligentActionOn);
Chris@70 1408 }
Chris@70 1409
Chris@70 1410 void
Chris@70 1411 MainWindow::toolFreeEditSelected()
Chris@70 1412 {
Chris@77 1413 m_viewManager->setToolMode(ViewManager::NoteEditMode);
Chris@70 1414 m_intelligentActionOn = false;
Chris@70 1415 m_analyser->setIntelligentActions(m_intelligentActionOn);
Chris@70 1416 }
Chris@70 1417
gyorgyf@21 1418 void
Chris@0 1419 MainWindow::updateMenuStates()
Chris@0 1420 {
Chris@0 1421 MainWindowBase::updateMenuStates();
Chris@0 1422
Chris@0 1423 Pane *currentPane = 0;
Chris@0 1424 Layer *currentLayer = 0;
Chris@0 1425
Chris@0 1426 if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
Chris@0 1427 if (currentPane) currentLayer = currentPane->getSelectedLayer();
Chris@0 1428
Chris@291 1429 bool haveMainModel =
Chris@291 1430 (getMainModel() != 0);
Chris@291 1431 bool havePlayTarget =
Chris@479 1432 (m_playTarget != 0 || m_audioIO != 0);
Chris@0 1433 bool haveCurrentPane =
Chris@0 1434 (currentPane != 0);
Chris@0 1435 bool haveCurrentLayer =
Chris@0 1436 (haveCurrentPane &&
Chris@0 1437 (currentLayer != 0));
Chris@0 1438 bool haveSelection =
Chris@187 1439 (m_viewManager &&
Chris@187 1440 !m_viewManager->getSelections().empty());
Chris@0 1441 bool haveCurrentTimeInstantsLayer =
Chris@187 1442 (haveCurrentLayer &&
Chris@187 1443 qobject_cast<TimeInstantLayer *>(currentLayer));
Chris@0 1444 bool haveCurrentTimeValueLayer =
Chris@187 1445 (haveCurrentLayer &&
Chris@187 1446 qobject_cast<TimeValueLayer *>(currentLayer));
Chris@187 1447 bool pitchCandidatesVisible =
Chris@187 1448 m_analyser->arePitchCandidatesShown();
Chris@0 1449
Chris@0 1450 emit canChangePlaybackSpeed(true);
Chris@0 1451 int v = m_playSpeed->value();
Chris@0 1452 emit canSpeedUpPlayback(v < m_playSpeed->maximum());
Chris@0 1453 emit canSlowDownPlayback(v > m_playSpeed->minimum());
Chris@0 1454
Chris@291 1455 bool haveWaveform =
Chris@291 1456 m_analyser->isVisible(Analyser::Audio) &&
Chris@291 1457 m_analyser->getLayer(Analyser::Audio);
Chris@291 1458
Chris@237 1459 bool havePitchTrack =
Chris@237 1460 m_analyser->isVisible(Analyser::PitchTrack) &&
Chris@237 1461 m_analyser->getLayer(Analyser::PitchTrack);
Chris@237 1462
Chris@237 1463 bool haveNotes =
Chris@237 1464 m_analyser->isVisible(Analyser::Notes) &&
Chris@237 1465 m_analyser->getLayer(Analyser::Notes);
Chris@237 1466
Chris@237 1467 emit canExportPitchTrack(havePitchTrack);
Chris@237 1468 emit canExportNotes(haveNotes);
Chris@237 1469 emit canSnapNotes(haveSelection && haveNotes);
Chris@224 1470
Chris@291 1471 emit canPlayWaveform(haveWaveform && haveMainModel && havePlayTarget);
Chris@291 1472 emit canPlayPitch(havePitchTrack && haveMainModel && havePlayTarget);
Chris@291 1473 emit canPlayNotes(haveNotes && haveMainModel && havePlayTarget);
Chris@291 1474
Chris@187 1475 if (pitchCandidatesVisible) {
Chris@187 1476 m_showCandidatesAction->setText(tr("Hide Pitch Candidates"));
Chris@187 1477 m_showCandidatesAction->setStatusTip(tr("Remove the display of alternate pitch candidates for the selected region"));
Chris@187 1478 } else {
Chris@187 1479 m_showCandidatesAction->setText(tr("Show Pitch Candidates"));
Chris@187 1480 m_showCandidatesAction->setStatusTip(tr("Show alternate pitch candidates for the selected region"));
Chris@187 1481 }
Chris@187 1482
Chris@0 1483 if (m_ffwdAction && m_rwdAction) {
Chris@0 1484 if (haveCurrentTimeInstantsLayer) {
Chris@0 1485 m_ffwdAction->setText(tr("Fast Forward to Next Instant"));
Chris@0 1486 m_ffwdAction->setStatusTip(tr("Fast forward to the next time instant in the current layer"));
Chris@0 1487 m_rwdAction->setText(tr("Rewind to Previous Instant"));
Chris@0 1488 m_rwdAction->setStatusTip(tr("Rewind to the previous time instant in the current layer"));
Chris@0 1489 } else if (haveCurrentTimeValueLayer) {
Chris@0 1490 m_ffwdAction->setText(tr("Fast Forward to Next Point"));
Chris@0 1491 m_ffwdAction->setStatusTip(tr("Fast forward to the next point in the current layer"));
Chris@0 1492 m_rwdAction->setText(tr("Rewind to Previous Point"));
Chris@0 1493 m_rwdAction->setStatusTip(tr("Rewind to the previous point in the current layer"));
Chris@0 1494 } else {
Chris@0 1495 m_ffwdAction->setText(tr("Fast Forward"));
Chris@0 1496 m_ffwdAction->setStatusTip(tr("Fast forward"));
Chris@0 1497 m_rwdAction->setText(tr("Rewind"));
Chris@0 1498 m_rwdAction->setStatusTip(tr("Rewind"));
Chris@0 1499 }
Chris@0 1500 }
Chris@0 1501 }
Chris@0 1502
Chris@0 1503 void
Chris@144 1504 MainWindow::showAudioToggled()
Chris@128 1505 {
Chris@144 1506 m_analyser->toggleVisible(Analyser::Audio);
justin@220 1507
Chris@424 1508 QSettings settings;
Chris@424 1509 settings.beginGroup("MainWindow");
Chris@424 1510
Chris@424 1511 bool playOn = false;
Chris@424 1512 if (m_analyser->isVisible(Analyser::Audio)) {
Chris@424 1513 // just switched layer on; check whether playback was also on previously
Chris@424 1514 playOn = settings.value("playaudiowas", true).toBool();
Chris@424 1515 } else {
Chris@424 1516 settings.setValue("playaudiowas", m_playAudio->isChecked());
Chris@424 1517 }
Chris@291 1518 m_analyser->setAudible(Analyser::Audio, playOn);
Chris@424 1519
Chris@424 1520 settings.endGroup();
Chris@291 1521
Chris@291 1522 updateMenuStates();
Chris@424 1523 updateLayerStatuses();
Chris@128 1524 }
Chris@128 1525
Chris@128 1526 void
Chris@144 1527 MainWindow::showPitchToggled()
Chris@128 1528 {
Chris@144 1529 m_analyser->toggleVisible(Analyser::PitchTrack);
justin@185 1530
Chris@424 1531 QSettings settings;
Chris@424 1532 settings.beginGroup("MainWindow");
Chris@424 1533
Chris@424 1534 bool playOn = false;
Chris@424 1535 if (m_analyser->isVisible(Analyser::PitchTrack)) {
Chris@424 1536 // just switched layer on; check whether playback was also on previously
Chris@424 1537 playOn = settings.value("playpitchwas", true).toBool();
Chris@424 1538 } else {
Chris@424 1539 settings.setValue("playpitchwas", m_playPitch->isChecked());
Chris@424 1540 }
Chris@424 1541 m_analyser->setAudible(Analyser::PitchTrack, playOn);
Chris@424 1542
Chris@424 1543 settings.endGroup();
Chris@291 1544
Chris@291 1545 updateMenuStates();
Chris@424 1546 updateLayerStatuses();
Chris@128 1547 }
Chris@128 1548
Chris@128 1549 void
Chris@145 1550 MainWindow::showSpectToggled()
Chris@145 1551 {
Chris@145 1552 m_analyser->toggleVisible(Analyser::Spectrogram);
Chris@145 1553 }
Chris@145 1554
Chris@145 1555 void
Chris@144 1556 MainWindow::showNotesToggled()
Chris@128 1557 {
Chris@144 1558 m_analyser->toggleVisible(Analyser::Notes);
justin@220 1559
Chris@424 1560 QSettings settings;
Chris@424 1561 settings.beginGroup("MainWindow");
Chris@424 1562
Chris@424 1563 bool playOn = false;
Chris@424 1564 if (m_analyser->isVisible(Analyser::Notes)) {
Chris@424 1565 // just switched layer on; check whether playback was also on previously
Chris@424 1566 playOn = settings.value("playnoteswas", true).toBool();
Chris@424 1567 } else {
Chris@424 1568 settings.setValue("playnoteswas", m_playNotes->isChecked());
Chris@424 1569 }
Chris@424 1570 m_analyser->setAudible(Analyser::Notes, playOn);
Chris@424 1571
Chris@424 1572 settings.endGroup();
Chris@291 1573
Chris@291 1574 updateMenuStates();
Chris@424 1575 updateLayerStatuses();
Chris@144 1576 }
Chris@144 1577
Chris@144 1578 void
Chris@144 1579 MainWindow::playAudioToggled()
Chris@144 1580 {
Chris@144 1581 m_analyser->toggleAudible(Analyser::Audio);
Chris@424 1582 updateLayerStatuses();
Chris@144 1583 }
Chris@144 1584
Chris@144 1585 void
Chris@144 1586 MainWindow::playPitchToggled()
Chris@144 1587 {
Chris@144 1588 m_analyser->toggleAudible(Analyser::PitchTrack);
Chris@424 1589 updateLayerStatuses();
Chris@144 1590 }
Chris@144 1591
Chris@144 1592 void
Chris@144 1593 MainWindow::playNotesToggled()
Chris@144 1594 {
Chris@144 1595 m_analyser->toggleAudible(Analyser::Notes);
Chris@424 1596 updateLayerStatuses();
Chris@144 1597 }
Chris@144 1598
Chris@144 1599 void
Chris@144 1600 MainWindow::updateLayerStatuses()
Chris@144 1601 {
Chris@144 1602 m_showAudio->setChecked(m_analyser->isVisible(Analyser::Audio));
Chris@424 1603 m_playAudio->setChecked(m_analyser->isAudible(Analyser::Audio));
Chris@424 1604 m_audioLPW->setEnabled(m_analyser->isAudible(Analyser::Audio));
Chris@405 1605 m_audioLPW->setLevel(m_analyser->getGain(Analyser::Audio));
Chris@405 1606 m_audioLPW->setPan(m_analyser->getPan(Analyser::Audio));
Chris@424 1607
Chris@405 1608 m_showPitch->setChecked(m_analyser->isVisible(Analyser::PitchTrack));
Chris@424 1609 m_playPitch->setChecked(m_analyser->isAudible(Analyser::PitchTrack));
Chris@424 1610 m_pitchLPW->setEnabled(m_analyser->isAudible(Analyser::PitchTrack));
Chris@405 1611 m_pitchLPW->setLevel(m_analyser->getGain(Analyser::PitchTrack));
Chris@405 1612 m_pitchLPW->setPan(m_analyser->getPan(Analyser::PitchTrack));
Chris@405 1613
Chris@405 1614 m_showNotes->setChecked(m_analyser->isVisible(Analyser::Notes));
Chris@424 1615 m_playNotes->setChecked(m_analyser->isAudible(Analyser::Notes));
Chris@424 1616 m_notesLPW->setEnabled(m_analyser->isAudible(Analyser::Notes));
Chris@405 1617 m_notesLPW->setLevel(m_analyser->getGain(Analyser::Notes));
Chris@405 1618 m_notesLPW->setPan(m_analyser->getPan(Analyser::Notes));
Chris@405 1619
Chris@145 1620 m_showSpect->setChecked(m_analyser->isVisible(Analyser::Spectrogram));
Chris@128 1621 }
Chris@128 1622
Chris@128 1623 void
Chris@227 1624 MainWindow::editDisplayExtents()
Chris@227 1625 {
Chris@399 1626 double min, max;
Chris@399 1627 double vmin = 0;
Chris@399 1628 double vmax = getMainModel()->getSampleRate() /2;
Chris@227 1629
Chris@227 1630 if (!m_analyser->getDisplayFrequencyExtents(min, max)) {
Chris@227 1631 //!!!
Chris@227 1632 return;
Chris@227 1633 }
Chris@227 1634
Chris@227 1635 RangeInputDialog dialog(tr("Set frequency range"),
Chris@227 1636 tr("Enter new frequency range, from %1 to %2 Hz.\nThese values will be rounded to the nearest spectrogram bin.")
Chris@227 1637 .arg(vmin).arg(vmax),
Chris@399 1638 "Hz", float(vmin), float(vmax), this);
Chris@399 1639 dialog.setRange(float(min), float(max));
Chris@227 1640
Chris@227 1641 if (dialog.exec() == QDialog::Accepted) {
Chris@399 1642 float fmin, fmax;
Chris@399 1643 dialog.getRange(fmin, fmax);
Chris@399 1644 min = fmin;
Chris@399 1645 max = fmax;
Chris@227 1646 if (min > max) {
Chris@399 1647 double tmp = max;
Chris@227 1648 max = min;
Chris@227 1649 min = tmp;
Chris@227 1650 }
Chris@227 1651 m_analyser->setDisplayFrequencyExtents(min, max);
Chris@227 1652 }
Chris@227 1653 }
Chris@227 1654
Chris@227 1655 void
Chris@0 1656 MainWindow::updateDescriptionLabel()
Chris@0 1657 {
Chris@6 1658 // Nothing, we don't have one
Chris@0 1659 }
Chris@0 1660
Chris@0 1661 void
Chris@0 1662 MainWindow::documentModified()
Chris@0 1663 {
Chris@0 1664 MainWindowBase::documentModified();
Chris@0 1665 }
Chris@0 1666
Chris@0 1667 void
Chris@0 1668 MainWindow::documentRestored()
Chris@0 1669 {
Chris@0 1670 MainWindowBase::documentRestored();
Chris@0 1671 }
Chris@0 1672
Chris@0 1673 void
Chris@0 1674 MainWindow::newSession()
Chris@0 1675 {
Chris@0 1676 if (!checkSaveModified()) return;
Chris@0 1677
Chris@0 1678 closeSession();
Chris@0 1679 createDocument();
Chris@0 1680 m_document->setAutoAlignment(true);
Chris@0 1681
Chris@604 1682 Pane *pane = m_paneStack->addPane();
Chris@362 1683 pane->setPlaybackFollow(PlaybackScrollPage);
Chris@0 1684
Chris@391 1685 m_viewManager->setGlobalCentreFrame
Chris@391 1686 (pane->getFrameForX(width() / 2));
Chris@391 1687
Chris@0 1688 connect(pane, SIGNAL(contextHelpChanged(const QString &)),
Chris@0 1689 this, SLOT(contextHelpChanged(const QString &)));
Chris@0 1690
Chris@6 1691 // Layer *waveform = m_document->createMainModelLayer(LayerFactory::Waveform);
Chris@6 1692 // m_document->addLayerToView(pane, waveform);
Chris@0 1693
Chris@0 1694 m_overview->registerView(pane);
Chris@0 1695
Chris@0 1696 CommandHistory::getInstance()->clear();
Chris@0 1697 CommandHistory::getInstance()->documentSaved();
Chris@0 1698 documentRestored();
Chris@0 1699 updateMenuStates();
Chris@0 1700 }
Chris@0 1701
Chris@0 1702 void
Chris@244 1703 MainWindow::documentReplaced()
Chris@244 1704 {
Chris@244 1705 if (m_document) {
Chris@244 1706 connect(m_document, SIGNAL(activity(QString)),
Chris@244 1707 m_activityLog, SLOT(activityHappened(QString)));
Chris@244 1708 }
Chris@244 1709 }
Chris@244 1710
Chris@244 1711 void
Chris@0 1712 MainWindow::closeSession()
Chris@0 1713 {
Chris@0 1714 if (!checkSaveModified()) return;
Chris@0 1715
Chris@226 1716 m_analyser->fileClosed();
Chris@226 1717
Chris@0 1718 while (m_paneStack->getPaneCount() > 0) {
Chris@0 1719
Chris@167 1720 Pane *pane = m_paneStack->getPane(m_paneStack->getPaneCount() - 1);
Chris@0 1721
Chris@167 1722 while (pane->getLayerCount() > 0) {
Chris@167 1723 m_document->removeLayerFromView
Chris@167 1724 (pane, pane->getLayer(pane->getLayerCount() - 1));
Chris@167 1725 }
Chris@167 1726
Chris@167 1727 m_overview->unregisterView(pane);
Chris@167 1728 m_paneStack->deletePane(pane);
Chris@0 1729 }
Chris@0 1730
Chris@0 1731 while (m_paneStack->getHiddenPaneCount() > 0) {
Chris@0 1732
Chris@167 1733 Pane *pane = m_paneStack->getHiddenPane
Chris@167 1734 (m_paneStack->getHiddenPaneCount() - 1);
Chris@167 1735
Chris@167 1736 while (pane->getLayerCount() > 0) {
Chris@167 1737 m_document->removeLayerFromView
Chris@167 1738 (pane, pane->getLayer(pane->getLayerCount() - 1));
Chris@167 1739 }
Chris@167 1740
Chris@167 1741 m_overview->unregisterView(pane);
Chris@167 1742 m_paneStack->deletePane(pane);
Chris@0 1743 }
Chris@0 1744
Chris@0 1745 delete m_document;
Chris@0 1746 m_document = 0;
Chris@0 1747 m_viewManager->clearSelections();
Chris@0 1748 m_timeRulerLayer = 0; // document owned this
Chris@0 1749
Chris@0 1750 m_sessionFile = "";
Chris@0 1751
Chris@0 1752 CommandHistory::getInstance()->clear();
Chris@0 1753 CommandHistory::getInstance()->documentSaved();
Chris@0 1754 documentRestored();
Chris@0 1755 }
Chris@0 1756
Chris@0 1757 void
Chris@0 1758 MainWindow::openFile()
Chris@0 1759 {
Chris@0 1760 QString orig = m_audioFile;
Chris@0 1761 if (orig == "") orig = ".";
Chris@0 1762 else orig = QFileInfo(orig).absoluteDir().canonicalPath();
Chris@0 1763
Chris@680 1764 QString path = getOpenFileName(FileFinder::SessionOrAudioFile);
Chris@0 1765
Chris@0 1766 if (path.isEmpty()) return;
Chris@0 1767
Chris@387 1768 FileOpenStatus status = openPath(path, ReplaceSession);
Chris@0 1769
Chris@0 1770 if (status == FileOpenFailed) {
Chris@0 1771 QMessageBox::critical(this, tr("Failed to open file"),
Chris@0 1772 tr("<b>File open failed</b><p>File \"%1\" could not be opened").arg(path));
Chris@0 1773 } else if (status == FileOpenWrongMode) {
Chris@0 1774 QMessageBox::critical(this, tr("Failed to open file"),
Chris@0 1775 tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
Chris@0 1776 }
Chris@0 1777 }
Chris@0 1778
Chris@0 1779 void
Chris@0 1780 MainWindow::openLocation()
Chris@0 1781 {
Chris@0 1782 QSettings settings;
Chris@0 1783 settings.beginGroup("MainWindow");
Chris@0 1784 QString lastLocation = settings.value("lastremote", "").toString();
Chris@0 1785
Chris@0 1786 bool ok = false;
Chris@0 1787 QString text = QInputDialog::getText
Chris@0 1788 (this, tr("Open Location"),
Chris@0 1789 tr("Please enter the URL of the location to open:"),
Chris@0 1790 QLineEdit::Normal, lastLocation, &ok);
Chris@0 1791
Chris@0 1792 if (!ok) return;
Chris@0 1793
Chris@0 1794 settings.setValue("lastremote", text);
Chris@0 1795
Chris@0 1796 if (text.isEmpty()) return;
Chris@0 1797
Chris@387 1798 FileOpenStatus status = openPath(text, ReplaceSession);
Chris@0 1799
Chris@0 1800 if (status == FileOpenFailed) {
Chris@0 1801 QMessageBox::critical(this, tr("Failed to open location"),
Chris@0 1802 tr("<b>Open failed</b><p>URL \"%1\" could not be opened").arg(text));
Chris@0 1803 } else if (status == FileOpenWrongMode) {
Chris@0 1804 QMessageBox::critical(this, tr("Failed to open location"),
Chris@0 1805 tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
Chris@0 1806 }
Chris@0 1807 }
Chris@0 1808
Chris@0 1809 void
Chris@0 1810 MainWindow::openRecentFile()
Chris@0 1811 {
Chris@0 1812 QObject *obj = sender();
Chris@3 1813 QAction *action = qobject_cast<QAction *>(obj);
Chris@0 1814
Chris@0 1815 if (!action) {
Chris@188 1816 cerr << "WARNING: MainWindow::openRecentFile: sender is not an action"
Chris@188 1817 << endl;
Chris@188 1818 return;
Chris@0 1819 }
Chris@0 1820
Chris@0 1821 QString path = action->text();
Chris@0 1822 if (path == "") return;
Chris@0 1823
Chris@387 1824 FileOpenStatus status = openPath(path, ReplaceSession);
Chris@0 1825
Chris@0 1826 if (status == FileOpenFailed) {
Chris@0 1827 QMessageBox::critical(this, tr("Failed to open location"),
Chris@0 1828 tr("<b>Open failed</b><p>File or URL \"%1\" could not be opened").arg(path));
Chris@0 1829 } else if (status == FileOpenWrongMode) {
Chris@0 1830 QMessageBox::critical(this, tr("Failed to open location"),
Chris@0 1831 tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
Chris@0 1832 }
Chris@0 1833 }
Chris@0 1834
Chris@0 1835 void
Chris@0 1836 MainWindow::paneAdded(Pane *pane)
Chris@0 1837 {
Chris@6 1838 pane->setPlaybackFollow(PlaybackScrollPage);
Chris@0 1839 m_paneStack->sizePanesEqually();
Chris@0 1840 if (m_overview) m_overview->registerView(pane);
Chris@0 1841 }
Chris@0 1842
Chris@0 1843 void
Chris@0 1844 MainWindow::paneHidden(Pane *pane)
Chris@0 1845 {
Chris@0 1846 if (m_overview) m_overview->unregisterView(pane);
Chris@0 1847 }
Chris@0 1848
Chris@0 1849 void
Chris@0 1850 MainWindow::paneAboutToBeDeleted(Pane *pane)
Chris@0 1851 {
Chris@0 1852 if (m_overview) m_overview->unregisterView(pane);
Chris@0 1853 }
Chris@0 1854
Chris@0 1855 void
Chris@0 1856 MainWindow::paneDropAccepted(Pane *pane, QStringList uriList)
Chris@0 1857 {
Chris@4 1858 if (pane) m_paneStack->setCurrentPane(pane);
Chris@0 1859
Chris@0 1860 for (QStringList::iterator i = uriList.begin(); i != uriList.end(); ++i) {
Chris@0 1861
Chris@387 1862 FileOpenStatus status = openPath(*i, ReplaceSession);
Chris@0 1863
Chris@0 1864 if (status == FileOpenFailed) {
Chris@0 1865 QMessageBox::critical(this, tr("Failed to open dropped URL"),
Chris@0 1866 tr("<b>Open failed</b><p>Dropped URL \"%1\" could not be opened").arg(*i));
Chris@0 1867 } else if (status == FileOpenWrongMode) {
Chris@0 1868 QMessageBox::critical(this, tr("Failed to open dropped URL"),
Chris@0 1869 tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
Chris@0 1870 }
Chris@0 1871 }
Chris@0 1872 }
Chris@0 1873
Chris@0 1874 void
Chris@0 1875 MainWindow::paneDropAccepted(Pane *pane, QString text)
Chris@0 1876 {
Chris@0 1877 if (pane) m_paneStack->setCurrentPane(pane);
Chris@0 1878
Chris@0 1879 QUrl testUrl(text);
Chris@0 1880 if (testUrl.scheme() == "file" ||
Chris@0 1881 testUrl.scheme() == "http" ||
Chris@0 1882 testUrl.scheme() == "ftp") {
Chris@0 1883 QStringList list;
Chris@0 1884 list.push_back(text);
Chris@0 1885 paneDropAccepted(pane, list);
Chris@0 1886 return;
Chris@0 1887 }
Chris@0 1888
Chris@0 1889 //!!! open as text -- but by importing as if a CSV, or just adding
Chris@0 1890 //to a text layer?
Chris@0 1891 }
Chris@0 1892
Chris@0 1893 void
Chris@0 1894 MainWindow::closeEvent(QCloseEvent *e)
Chris@0 1895 {
Chris@70 1896 // cerr << "MainWindow::closeEvent" << endl;
Chris@0 1897
Chris@0 1898 if (m_openingAudioFile) {
Chris@70 1899 // cerr << "Busy - ignoring close event" << endl;
Chris@257 1900 e->ignore();
Chris@257 1901 return;
Chris@0 1902 }
Chris@0 1903
Chris@688 1904 if (!checkSaveModified()) {
Chris@70 1905 // cerr << "Ignoring close event" << endl;
Chris@257 1906 e->ignore();
Chris@257 1907 return;
Chris@0 1908 }
Chris@0 1909
Chris@0 1910 QSettings settings;
Chris@0 1911 settings.beginGroup("MainWindow");
Chris@0 1912 settings.setValue("size", size());
Chris@0 1913 settings.setValue("position", pos());
Chris@0 1914 settings.endGroup();
Chris@0 1915
Chris@0 1916 delete m_keyReference;
Chris@0 1917 m_keyReference = 0;
Chris@0 1918
Chris@0 1919 closeSession();
Chris@0 1920
Chris@0 1921 e->accept();
Chris@0 1922 return;
Chris@0 1923 }
Chris@0 1924
Chris@0 1925 bool
Chris@0 1926 MainWindow::commitData(bool mayAskUser)
Chris@0 1927 {
Chris@0 1928 if (mayAskUser) {
Chris@0 1929 bool rv = checkSaveModified();
Chris@0 1930 return rv;
Chris@0 1931 } else {
Chris@0 1932 if (!m_documentModified) return true;
Chris@0 1933
Chris@0 1934 // If we can't check with the user first, then we can't save
Chris@0 1935 // to the original session file (even if we have it) -- have
Chris@0 1936 // to use a temporary file
Chris@0 1937
Chris@0 1938 QString svDirBase = ".sv1";
Chris@0 1939 QString svDir = QDir::home().filePath(svDirBase);
Chris@0 1940
Chris@0 1941 if (!QFileInfo(svDir).exists()) {
Chris@0 1942 if (!QDir::home().mkdir(svDirBase)) return false;
Chris@0 1943 } else {
Chris@0 1944 if (!QFileInfo(svDir).isDir()) return false;
Chris@0 1945 }
Chris@0 1946
Chris@0 1947 // This name doesn't have to be unguessable
Chris@0 1948 #ifndef _WIN32
Chris@0 1949 QString fname = QString("tmp-%1-%2.sv")
Chris@0 1950 .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz"))
Chris@0 1951 .arg(QProcess().pid());
Chris@0 1952 #else
Chris@0 1953 QString fname = QString("tmp-%1.sv")
Chris@0 1954 .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz"));
Chris@0 1955 #endif
Chris@0 1956 QString fpath = QDir(svDir).filePath(fname);
Chris@0 1957 if (saveSessionFile(fpath)) {
Chris@0 1958 m_recentFiles.addFile(fpath);
Chris@0 1959 return true;
Chris@0 1960 } else {
Chris@0 1961 return false;
Chris@0 1962 }
Chris@0 1963 }
Chris@0 1964 }
Chris@0 1965
Chris@0 1966 bool
Chris@0 1967 MainWindow::checkSaveModified()
Chris@0 1968 {
Chris@0 1969 // Called before some destructive operation (e.g. new session,
Chris@0 1970 // exit program). Return true if we can safely proceed, false to
Chris@0 1971 // cancel.
Chris@0 1972
Chris@0 1973 if (!m_documentModified) return true;
Chris@0 1974
Chris@0 1975 int button =
Chris@257 1976 QMessageBox::warning(this,
Chris@257 1977 tr("Session modified"),
Chris@257 1978 tr("The current session has been modified.\nDo you want to save it?"),
Chris@257 1979 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@0 1980 QMessageBox::Yes);
Chris@0 1981
Chris@0 1982 if (button == QMessageBox::Yes) {
Chris@257 1983 saveSession();
Chris@257 1984 if (m_documentModified) { // save failed -- don't proceed!
Chris@257 1985 return false;
Chris@257 1986 } else {
Chris@0 1987 return true; // saved, so it's safe to continue now
Chris@0 1988 }
Chris@0 1989 } else if (button == QMessageBox::No) {
Chris@257 1990 m_documentModified = false; // so we know to abandon it
Chris@257 1991 return true;
Chris@0 1992 }
Chris@0 1993
Chris@0 1994 // else cancel
Chris@0 1995 return false;
Chris@0 1996 }
Chris@0 1997
Chris@314 1998 bool
Chris@314 1999 MainWindow::waitForInitialAnalysis()
Chris@314 2000 {
Chris@314 2001 // Called before saving a session. We can't safely save while the
Chris@314 2002 // initial analysis is happening, because then we end up with an
Chris@314 2003 // incomplete session on reload. There are certainly theoretically
Chris@314 2004 // better ways to handle this...
Chris@314 2005
Chris@330 2006 QSettings settings;
Chris@330 2007 settings.beginGroup("Analyser");
Chris@330 2008 bool autoAnalyse = settings.value("auto-analysis", true).toBool();
Chris@330 2009 settings.endGroup();
Chris@330 2010
Chris@330 2011 if (!autoAnalyse) {
Chris@330 2012 return true;
Chris@330 2013 }
Chris@330 2014
Chris@314 2015 if (!m_analyser || m_analyser->getInitialAnalysisCompletion() >= 100) {
Chris@314 2016 return true;
Chris@314 2017 }
Chris@314 2018
Chris@314 2019 QMessageBox mb(QMessageBox::Information,
Chris@314 2020 tr("Waiting for analysis"),
Chris@442 2021 tr("Waiting for initial analysis to finish before loading or saving..."),
Chris@314 2022 QMessageBox::Cancel,
Chris@314 2023 this);
Chris@314 2024
Chris@314 2025 connect(m_analyser, SIGNAL(initialAnalysisCompleted()),
Chris@314 2026 &mb, SLOT(accept()));
Chris@314 2027
Chris@314 2028 if (mb.exec() == QDialog::Accepted) {
Chris@314 2029 return true;
Chris@314 2030 } else {
Chris@314 2031 return false;
Chris@314 2032 }
Chris@314 2033 }
Chris@314 2034
Chris@0 2035 void
Chris@0 2036 MainWindow::saveSession()
Chris@0 2037 {
Chris@269 2038 // We do not want to save mid-analysis regions -- that would cause
Chris@269 2039 // confusion on reloading
Chris@269 2040 m_analyser->clearReAnalysis();
Chris@269 2041 clearSelection();
Chris@269 2042
Chris@0 2043 if (m_sessionFile != "") {
Chris@257 2044 if (!saveSessionFile(m_sessionFile)) {
Chris@257 2045 QMessageBox::critical
Chris@257 2046 (this, tr("Failed to save file"),
Chris@257 2047 tr("Session file \"%1\" could not be saved.").arg(m_sessionFile));
Chris@257 2048 } else {
Chris@257 2049 CommandHistory::getInstance()->documentSaved();
Chris@257 2050 documentRestored();
Chris@257 2051 }
Chris@0 2052 } else {
Chris@257 2053 saveSessionAs();
Chris@0 2054 }
Chris@0 2055 }
Chris@0 2056
Chris@0 2057 void
matthiasm@310 2058 MainWindow::saveSessionInAudioPath()
matthiasm@310 2059 {
matthiasm@311 2060 if (m_audioFile == "") return;
matthiasm@311 2061
Chris@314 2062 if (!waitForInitialAnalysis()) return;
Chris@314 2063
matthiasm@310 2064 // We do not want to save mid-analysis regions -- that would cause
matthiasm@310 2065 // confusion on reloading
matthiasm@310 2066 m_analyser->clearReAnalysis();
matthiasm@310 2067 clearSelection();
matthiasm@310 2068
matthiasm@310 2069 QString filepath = QFileInfo(m_audioFile).absoluteDir().canonicalPath();
matthiasm@310 2070 QString basename = QFileInfo(m_audioFile).completeBaseName();
matthiasm@310 2071
matthiasm@310 2072 QString path = QDir(filepath).filePath(basename + ".ton");
matthiasm@310 2073
matthiasm@310 2074 cerr << path << endl;
matthiasm@310 2075
Chris@313 2076 // We don't want to overwrite an existing .ton file unless we put
Chris@313 2077 // it there in the first place
Chris@313 2078 bool shouldVerify = true;
Chris@313 2079 if (m_sessionFile == path) {
Chris@313 2080 shouldVerify = false;
Chris@313 2081 }
Chris@313 2082
Chris@313 2083 if (shouldVerify && QFileInfo(path).exists()) {
Chris@313 2084 if (QMessageBox::question(0, tr("File exists"),
Chris@313 2085 tr("<b>File exists</b><p>The file \"%1\" already exists.\nDo you want to overwrite it?").arg(path),
Chris@313 2086 QMessageBox::Ok,
Chris@313 2087 QMessageBox::Cancel) != QMessageBox::Ok) {
Chris@313 2088 return;
Chris@313 2089 }
Chris@313 2090 }
Chris@313 2091
Chris@314 2092 if (!waitForInitialAnalysis()) {
Chris@314 2093 QMessageBox::warning(this, tr("File not saved"),
Chris@314 2094 tr("Wait cancelled: the session has not been saved."));
Chris@314 2095 }
Chris@314 2096
matthiasm@310 2097 if (!saveSessionFile(path)) {
matthiasm@310 2098 QMessageBox::critical(this, tr("Failed to save file"),
matthiasm@310 2099 tr("Session file \"%1\" could not be saved.").arg(path));
matthiasm@310 2100 } else {
matthiasm@310 2101 setWindowTitle(tr("%1: %2")
matthiasm@310 2102 .arg(QApplication::applicationName())
matthiasm@310 2103 .arg(QFileInfo(path).fileName()));
matthiasm@310 2104 m_sessionFile = path;
matthiasm@310 2105 CommandHistory::getInstance()->documentSaved();
matthiasm@310 2106 documentRestored();
matthiasm@310 2107 m_recentFiles.addFile(path);
matthiasm@310 2108 }
matthiasm@310 2109 }
matthiasm@310 2110
matthiasm@310 2111 void
Chris@0 2112 MainWindow::saveSessionAs()
Chris@0 2113 {
Chris@270 2114 // We do not want to save mid-analysis regions -- that would cause
Chris@270 2115 // confusion on reloading
Chris@270 2116 m_analyser->clearReAnalysis();
Chris@270 2117 clearSelection();
Chris@270 2118
Chris@0 2119 QString path = getSaveFileName(FileFinder::SessionFile);
Chris@0 2120
Chris@309 2121 if (path == "") {
Chris@309 2122 return;
Chris@309 2123 }
Chris@0 2124
Chris@314 2125 if (!waitForInitialAnalysis()) {
Chris@314 2126 QMessageBox::warning(this, tr("File not saved"),
Chris@314 2127 tr("Wait cancelled: the session has not been saved."));
Chris@314 2128 return;
Chris@314 2129 }
Chris@314 2130
Chris@0 2131 if (!saveSessionFile(path)) {
Chris@85 2132 QMessageBox::critical(this, tr("Failed to save file"),
Chris@85 2133 tr("Session file \"%1\" could not be saved.").arg(path));
Chris@0 2134 } else {
Chris@85 2135 setWindowTitle(tr("%1: %2")
Chris@0 2136 .arg(QApplication::applicationName())
Chris@85 2137 .arg(QFileInfo(path).fileName()));
Chris@85 2138 m_sessionFile = path;
Chris@85 2139 CommandHistory::getInstance()->documentSaved();
Chris@85 2140 documentRestored();
Chris@0 2141 m_recentFiles.addFile(path);
Chris@0 2142 }
Chris@0 2143 }
Chris@0 2144
Chris@85 2145 QString
Chris@85 2146 MainWindow::exportToSVL(QString path, Layer *layer)
Chris@85 2147 {
Chris@573 2148 auto model = ModelById::get(layer->getModel());
Chris@573 2149 if (!model) return "Internal error: No model in layer";
Chris@85 2150
Chris@85 2151 QFile file(path);
Chris@85 2152 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
Chris@85 2153 return tr("Failed to open file %1 for writing").arg(path);
Chris@85 2154 } else {
Chris@85 2155 QTextStream out(&file);
Chris@85 2156 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
Chris@85 2157 << "<!DOCTYPE sonic-visualiser>\n"
Chris@85 2158 << "<sv>\n"
Chris@85 2159 << " <data>\n";
Chris@85 2160
Chris@85 2161 model->toXml(out, " ");
Chris@85 2162
Chris@85 2163 out << " </data>\n"
Chris@85 2164 << " <display>\n";
Chris@85 2165
Chris@85 2166 layer->toXml(out, " ");
Chris@85 2167
Chris@85 2168 out << " </display>\n"
Chris@85 2169 << "</sv>\n";
Chris@85 2170
Chris@85 2171 return "";
Chris@85 2172 }
Chris@85 2173 }
Chris@85 2174
Chris@0 2175 void
Chris@174 2176 MainWindow::importPitchLayer()
Chris@174 2177 {
Chris@174 2178 QString path = getOpenFileName(FileFinder::LayerFileNoMidiNonSV);
Chris@174 2179 if (path == "") return;
Chris@174 2180
Chris@174 2181 FileOpenStatus status = importPitchLayer(path);
Chris@174 2182
Chris@174 2183 if (status == FileOpenFailed) {
Chris@174 2184 emit hideSplash();
Chris@174 2185 QMessageBox::critical(this, tr("Failed to open file"),
Chris@174 2186 tr("<b>File open failed</b><p>Layer file %1 could not be opened.").arg(path));
Chris@174 2187 return;
Chris@174 2188 } else if (status == FileOpenWrongMode) {
Chris@174 2189 emit hideSplash();
Chris@174 2190 QMessageBox::critical(this, tr("Failed to open file"),
Chris@174 2191 tr("<b>Audio required</b><p>Unable to load layer data from \"%1\" without an audio file.<br>Please load at least one audio file before importing annotations.").arg(path));
Chris@174 2192 }
Chris@174 2193 }
Chris@174 2194
Chris@174 2195 MainWindow::FileOpenStatus
Chris@174 2196 MainWindow::importPitchLayer(FileSource source)
Chris@174 2197 {
Chris@174 2198 if (!source.isAvailable()) return FileOpenFailed;
Chris@174 2199 source.waitForData();
Chris@174 2200
Chris@442 2201 if (!waitForInitialAnalysis()) return FileOpenCancelled;
Chris@442 2202
Chris@174 2203 QString path = source.getLocalFilename();
Chris@174 2204
Chris@174 2205 RDFImporter::RDFDocumentType rdfType =
Chris@174 2206 RDFImporter::identifyDocumentType(QUrl::fromLocalFile(path).toString());
Chris@174 2207
Chris@174 2208 if (rdfType != RDFImporter::NotRDF) {
Chris@174 2209
Chris@174 2210 //!!!
Chris@174 2211 return FileOpenFailed;
Chris@174 2212
Chris@174 2213 } else if (source.getExtension().toLower() == "svl" ||
Chris@174 2214 (source.getExtension().toLower() == "xml" &&
Chris@174 2215 (SVFileReader::identifyXmlFile(source.getLocalFilename())
Chris@174 2216 == SVFileReader::SVLayerFile))) {
Chris@174 2217
Chris@174 2218 //!!!
Chris@174 2219 return FileOpenFailed;
Chris@174 2220
Chris@174 2221 } else {
Chris@174 2222
Chris@174 2223 try {
Chris@174 2224
Chris@174 2225 CSVFormat format(path);
Chris@174 2226 format.setSampleRate(getMainModel()->getSampleRate());
Chris@174 2227
Chris@174 2228 if (format.getModelType() != CSVFormat::TwoDimensionalModel) {
Chris@174 2229 //!!! error report
Chris@174 2230 return FileOpenFailed;
Chris@174 2231 }
Chris@174 2232
Chris@174 2233 Model *model = DataFileReaderFactory::loadCSV
Chris@174 2234 (path, format, getMainModel()->getSampleRate());
Chris@174 2235
Chris@174 2236 if (model) {
Chris@174 2237
Chris@174 2238 SVDEBUG << "MainWindow::importPitchLayer: Have model" << endl;
Chris@174 2239
Chris@573 2240 ModelId modelId = ModelById::add
Chris@573 2241 (std::shared_ptr<Model>(model));
Chris@573 2242
Chris@174 2243 CommandHistory::getInstance()->startCompoundOperation
Chris@174 2244 (tr("Import Pitch Track"), true);
Chris@174 2245
Chris@573 2246 Layer *newLayer = m_document->createImportedLayer(modelId);
Chris@174 2247
Chris@174 2248 m_analyser->takePitchTrackFrom(newLayer);
Chris@174 2249
Chris@174 2250 m_document->deleteLayer(newLayer);
Chris@174 2251
Chris@174 2252 CommandHistory::getInstance()->endCompoundOperation();
Chris@174 2253
Chris@174 2254 if (!source.isRemote()) {
Chris@174 2255 registerLastOpenedFilePath
Chris@174 2256 (FileFinder::LayerFile,
Chris@174 2257 path); // for file dialog
Chris@174 2258 }
Chris@174 2259
Chris@174 2260 return FileOpenSucceeded;
Chris@174 2261 }
Chris@174 2262 } catch (DataFileReaderFactory::Exception e) {
Chris@174 2263 if (e == DataFileReaderFactory::ImportCancelled) {
Chris@174 2264 return FileOpenCancelled;
Chris@174 2265 }
Chris@174 2266 }
Chris@174 2267 }
Chris@174 2268
Chris@174 2269 return FileOpenFailed;
Chris@174 2270 }
Chris@174 2271
Chris@174 2272 void
Chris@85 2273 MainWindow::exportPitchLayer()
matthiasm@26 2274 {
Chris@174 2275 Layer *layer = m_analyser->getLayer(Analyser::PitchTrack);
matthiasm@26 2276 if (!layer) return;
matthiasm@26 2277
Chris@573 2278 auto model = ModelById::getAs<SparseTimeValueModel>(layer->getModel());
matthiasm@26 2279 if (!model) return;
matthiasm@26 2280
Chris@96 2281 FileFinder::FileType type = FileFinder::LayerFileNoMidiNonSV;
matthiasm@26 2282
matthiasm@26 2283 QString path = getSaveFileName(type);
matthiasm@26 2284
matthiasm@26 2285 if (path == "") return;
matthiasm@26 2286
Chris@442 2287 if (!waitForInitialAnalysis()) return;
Chris@442 2288
matthiasm@26 2289 if (QFileInfo(path).suffix() == "") path += ".svl";
matthiasm@26 2290
matthiasm@26 2291 QString suffix = QFileInfo(path).suffix().toLower();
matthiasm@26 2292
matthiasm@26 2293 QString error;
matthiasm@26 2294
matthiasm@26 2295 if (suffix == "xml" || suffix == "svl") {
matthiasm@26 2296
Chris@85 2297 error = exportToSVL(path, layer);
matthiasm@26 2298
matthiasm@26 2299 } else if (suffix == "ttl" || suffix == "n3") {
matthiasm@26 2300
Chris@573 2301 RDFExporter exporter(path, model.get());
Chris@85 2302 exporter.write();
Chris@85 2303 if (!exporter.isOK()) {
Chris@85 2304 error = exporter.getError();
Chris@85 2305 }
Chris@85 2306
Chris@85 2307 } else {
Chris@85 2308
Chris@429 2309 DataExportOptions options = DataExportFillGaps;
Chris@429 2310
Chris@573 2311 CSVFileWriter writer(path, model.get(),
Chris@429 2312 ((suffix == "csv") ? "," : "\t"),
Chris@429 2313 options);
Chris@85 2314 writer.write();
Chris@85 2315
Chris@85 2316 if (!writer.isOK()) {
Chris@85 2317 error = writer.getError();
Chris@85 2318 }
Chris@85 2319 }
Chris@85 2320
Chris@85 2321 if (error != "") {
Chris@85 2322 QMessageBox::critical(this, tr("Failed to write file"), error);
Chris@85 2323 } else {
Chris@85 2324 emit activity(tr("Export layer to \"%1\"").arg(path));
Chris@85 2325 }
Chris@85 2326 }
Chris@85 2327
Chris@85 2328 void
Chris@85 2329 MainWindow::exportNoteLayer()
Chris@85 2330 {
Chris@174 2331 Layer *layer = m_analyser->getLayer(Analyser::Notes);
Chris@85 2332 if (!layer) return;
Chris@85 2333
Chris@573 2334 auto model = ModelById::getAs<NoteModel>(layer->getModel());
Chris@85 2335 if (!model) return;
Chris@85 2336
Chris@96 2337 FileFinder::FileType type = FileFinder::LayerFileNonSV;
Chris@85 2338
Chris@85 2339 QString path = getSaveFileName(type);
Chris@85 2340
Chris@85 2341 if (path == "") return;
Chris@85 2342
Chris@85 2343 if (QFileInfo(path).suffix() == "") path += ".svl";
Chris@85 2344
Chris@85 2345 QString suffix = QFileInfo(path).suffix().toLower();
Chris@85 2346
Chris@85 2347 QString error;
Chris@85 2348
Chris@85 2349 if (suffix == "xml" || suffix == "svl") {
Chris@85 2350
Chris@85 2351 error = exportToSVL(path, layer);
Chris@85 2352
Chris@85 2353 } else if (suffix == "mid" || suffix == "midi") {
Chris@85 2354
Chris@573 2355 MIDIFileWriter writer(path, model.get(), model->getSampleRate());
Chris@85 2356 writer.write();
Chris@85 2357 if (!writer.isOK()) {
Chris@85 2358 error = writer.getError();
Chris@85 2359 }
Chris@85 2360
Chris@85 2361 } else if (suffix == "ttl" || suffix == "n3") {
Chris@85 2362
Chris@573 2363 RDFExporter exporter(path, model.get());
Chris@85 2364 exporter.write();
Chris@85 2365 if (!exporter.isOK()) {
Chris@85 2366 error = exporter.getError();
matthiasm@26 2367 }
matthiasm@26 2368
matthiasm@26 2369 } else {
matthiasm@26 2370
Chris@688 2371 DataExportOptions options = DataExportOmitLevel;
Chris@429 2372
Chris@573 2373 CSVFileWriter writer(path, model.get(),
Chris@429 2374 ((suffix == "csv") ? "," : "\t"),
Chris@429 2375 options);
matthiasm@26 2376 writer.write();
matthiasm@26 2377
matthiasm@26 2378 if (!writer.isOK()) {
matthiasm@26 2379 error = writer.getError();
matthiasm@26 2380 }
matthiasm@26 2381 }
matthiasm@26 2382
matthiasm@26 2383 if (error != "") {
matthiasm@26 2384 QMessageBox::critical(this, tr("Failed to write file"), error);
matthiasm@26 2385 } else {
matthiasm@26 2386 emit activity(tr("Export layer to \"%1\"").arg(path));
matthiasm@26 2387 }
matthiasm@26 2388 }
matthiasm@26 2389
Chris@139 2390 void
Chris@485 2391 MainWindow::browseRecordedAudio()
Chris@485 2392 {
Chris@485 2393 if (!m_recordTarget) return;
Chris@485 2394
Chris@539 2395 QString path = RecordDirectory::getRecordContainerDirectory();
Chris@539 2396 if (path == "") path = RecordDirectory::getRecordDirectory();
Chris@485 2397 if (path == "") return;
Chris@485 2398
Chris@485 2399 openLocalFolder(path);
Chris@485 2400 }
Chris@485 2401
Chris@485 2402 void
Chris@399 2403 MainWindow::doubleClickSelectInvoked(sv_frame_t frame)
Chris@139 2404 {
Chris@399 2405 sv_frame_t f0, f1;
Chris@139 2406 m_analyser->getEnclosingSelectionScope(frame, f0, f1);
Chris@139 2407
Chris@139 2408 cerr << "MainWindow::doubleClickSelectInvoked(" << frame << "): [" << f0 << "," << f1 << "]" << endl;
Chris@139 2409
Chris@164 2410 Selection sel(f0, f1);
Chris@164 2411 m_viewManager->setSelection(sel);
Chris@164 2412 }
Chris@164 2413
Chris@164 2414 void
Chris@194 2415 MainWindow::abandonSelection()
Chris@167 2416 {
Chris@194 2417 // Named abandonSelection rather than clearSelection to indicate
Chris@194 2418 // that this is an active operation -- it restores the original
Chris@194 2419 // content of the pitch track in the selected region rather than
Chris@194 2420 // simply un-selecting.
Chris@194 2421
Chris@194 2422 cerr << "MainWindow::abandonSelection()" << endl;
Chris@167 2423
Chris@199 2424 CommandHistory::getInstance()->startCompoundOperation(tr("Abandon Selection"), true);
Chris@168 2425
Chris@194 2426 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@194 2427 if (!selections.empty()) {
Chris@194 2428 Selection sel = *selections.begin();
Chris@199 2429 m_analyser->abandonReAnalysis(sel);
Chris@252 2430 auxSnapNotes(sel);
Chris@194 2431 }
Chris@194 2432
Chris@167 2433 MainWindowBase::clearSelection();
Chris@168 2434
Chris@168 2435 CommandHistory::getInstance()->endCompoundOperation();
Chris@167 2436 }
Chris@167 2437
Chris@167 2438 void
Chris@192 2439 MainWindow::selectionChangedByUser()
Chris@164 2440 {
Chris@221 2441 if (!m_document) {
Chris@221 2442 // we're exiting, most likely
Chris@221 2443 return;
Chris@221 2444 }
Chris@221 2445
Chris@164 2446 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@164 2447
Chris@192 2448 cerr << "MainWindow::selectionChangedByUser" << endl;
Chris@192 2449
Chris@192 2450 m_analyser->showPitchCandidates(m_pendingConstraint.isConstrained());
Chris@190 2451
Chris@164 2452 if (!selections.empty()) {
Chris@164 2453 Selection sel = *selections.begin();
Chris@192 2454 cerr << "MainWindow::selectionChangedByUser: have selection" << endl;
Chris@192 2455 QString error = m_analyser->reAnalyseSelection
Chris@192 2456 (sel, m_pendingConstraint);
Chris@164 2457 if (error != "") {
Chris@164 2458 QMessageBox::critical
Chris@164 2459 (this, tr("Failed to analyse selection"),
Chris@164 2460 tr("<b>Analysis failed</b><p>%2</p>").arg(error));
Chris@164 2461 }
Chris@164 2462 }
Chris@192 2463
Chris@192 2464 m_pendingConstraint = Analyser::FrequencyRange();
Chris@192 2465 }
Chris@192 2466
Chris@192 2467 void
Chris@192 2468 MainWindow::regionOutlined(QRect r)
Chris@192 2469 {
Chris@192 2470 cerr << "MainWindow::regionOutlined(" << r.x() << "," << r.y() << "," << r.width() << "," << r.height() << ")" << endl;
Chris@192 2471
Chris@192 2472 Pane *pane = qobject_cast<Pane *>(sender());
Chris@192 2473 if (!pane) {
Chris@192 2474 cerr << "MainWindow::regionOutlined: not sent by pane, ignoring" << endl;
Chris@192 2475 return;
Chris@192 2476 }
Chris@192 2477
Chris@192 2478 if (!m_analyser) {
Chris@192 2479 cerr << "MainWindow::regionOutlined: no analyser, ignoring" << endl;
Chris@192 2480 return;
Chris@192 2481 }
Chris@192 2482
Chris@192 2483 SpectrogramLayer *spectrogram = qobject_cast<SpectrogramLayer *>
Chris@192 2484 (m_analyser->getLayer(Analyser::Spectrogram));
Chris@192 2485 if (!spectrogram) {
Chris@192 2486 cerr << "MainWindow::regionOutlined: no spectrogram layer, ignoring" << endl;
Chris@192 2487 return;
Chris@192 2488 }
Chris@192 2489
Chris@399 2490 sv_frame_t f0 = pane->getFrameForX(r.x());
Chris@399 2491 sv_frame_t f1 = pane->getFrameForX(r.x() + r.width());
Chris@192 2492
Chris@399 2493 double v0 = spectrogram->getFrequencyForY(pane, r.y() + r.height());
Chris@399 2494 double v1 = spectrogram->getFrequencyForY(pane, r.y());
Chris@192 2495
Chris@192 2496 cerr << "MainWindow::regionOutlined: frame " << f0 << " -> " << f1
Chris@192 2497 << ", frequency " << v0 << " -> " << v1 << endl;
Chris@192 2498
Chris@192 2499 m_pendingConstraint = Analyser::FrequencyRange(v0, v1);
Chris@192 2500
Chris@192 2501 Selection sel(f0, f1);
Chris@192 2502 m_viewManager->setSelection(sel);
Chris@0 2503 }
Chris@0 2504
Chris@0 2505 void
Chris@168 2506 MainWindow::clearPitches()
Chris@168 2507 {
Chris@168 2508 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@168 2509
Chris@168 2510 CommandHistory::getInstance()->startCompoundOperation(tr("Clear Pitches"), true);
Chris@168 2511
Chris@168 2512 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@168 2513 k != selections.end(); ++k) {
Chris@184 2514 m_analyser->deletePitches(*k);
Chris@252 2515 auxSnapNotes(*k);
Chris@168 2516 }
Chris@168 2517
Chris@168 2518 CommandHistory::getInstance()->endCompoundOperation();
Chris@168 2519 }
Chris@168 2520
Chris@168 2521 void
Chris@142 2522 MainWindow::octaveShift(bool up)
Chris@142 2523 {
Chris@142 2524 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@142 2525
Chris@211 2526 CommandHistory::getInstance()->startCompoundOperation
Chris@211 2527 (up ? tr("Choose Higher Octave") : tr("Choose Lower Octave"), true);
Chris@142 2528
Chris@168 2529 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@168 2530 k != selections.end(); ++k) {
Chris@142 2531
Chris@168 2532 m_analyser->shiftOctave(*k, up);
Chris@252 2533 auxSnapNotes(*k);
Chris@142 2534 }
Chris@142 2535
Chris@142 2536 CommandHistory::getInstance()->endCompoundOperation();
Chris@142 2537 }
Chris@142 2538
Chris@142 2539 void
Chris@184 2540 MainWindow::togglePitchCandidates()
Chris@184 2541 {
Chris@199 2542 CommandHistory::getInstance()->startCompoundOperation(tr("Toggle Pitch Candidates"), true);
Chris@199 2543
Chris@184 2544 m_analyser->showPitchCandidates(!m_analyser->arePitchCandidatesShown());
Chris@199 2545
Chris@199 2546 CommandHistory::getInstance()->endCompoundOperation();
Chris@199 2547
Chris@187 2548 updateMenuStates();
Chris@184 2549 }
Chris@184 2550
Chris@184 2551 void
Chris@184 2552 MainWindow::switchPitchUp()
Chris@167 2553 {
Chris@211 2554 if (m_analyser->arePitchCandidatesShown()) {
Chris@211 2555 if (m_analyser->haveHigherPitchCandidate()) {
Chris@211 2556
Chris@211 2557 CommandHistory::getInstance()->startCompoundOperation
Chris@211 2558 (tr("Choose Higher Pitch Candidate"), true);
Chris@211 2559
Chris@211 2560 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@211 2561
Chris@211 2562 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@211 2563 k != selections.end(); ++k) {
Chris@211 2564 m_analyser->switchPitchCandidate(*k, true);
Chris@252 2565 auxSnapNotes(*k);
Chris@211 2566 }
Chris@211 2567
Chris@211 2568 CommandHistory::getInstance()->endCompoundOperation();
Chris@211 2569 }
Chris@211 2570 } else {
Chris@211 2571 octaveShift(true);
Chris@167 2572 }
Chris@167 2573 }
Chris@167 2574
Chris@167 2575 void
Chris@184 2576 MainWindow::switchPitchDown()
Chris@184 2577 {
Chris@211 2578 if (m_analyser->arePitchCandidatesShown()) {
Chris@211 2579 if (m_analyser->haveLowerPitchCandidate()) {
Chris@211 2580
Chris@211 2581 CommandHistory::getInstance()->startCompoundOperation
Chris@211 2582 (tr("Choose Lower Pitch Candidate"), true);
Chris@211 2583
Chris@211 2584 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@211 2585
Chris@211 2586 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@211 2587 k != selections.end(); ++k) {
Chris@211 2588 m_analyser->switchPitchCandidate(*k, false);
Chris@252 2589 auxSnapNotes(*k);
Chris@211 2590 }
Chris@211 2591
Chris@211 2592 CommandHistory::getInstance()->endCompoundOperation();
Chris@211 2593 }
Chris@211 2594 } else {
Chris@211 2595 octaveShift(false);
Chris@184 2596 }
Chris@184 2597 }
Chris@184 2598
Chris@184 2599 void
Chris@237 2600 MainWindow::snapNotesToPitches()
Chris@237 2601 {
matthiasm@274 2602 cerr << "in snapNotesToPitches" << endl;
Chris@237 2603 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@237 2604
Chris@237 2605 if (!selections.empty()) {
Chris@237 2606
Chris@237 2607 CommandHistory::getInstance()->startCompoundOperation
Chris@237 2608 (tr("Snap Notes to Pitches"), true);
Chris@237 2609
Chris@237 2610 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@237 2611 k != selections.end(); ++k) {
Chris@239 2612 auxSnapNotes(*k);
Chris@237 2613 }
Chris@237 2614
Chris@237 2615 CommandHistory::getInstance()->endCompoundOperation();
Chris@237 2616 }
Chris@237 2617 }
Chris@237 2618
Chris@237 2619 void
Chris@239 2620 MainWindow::auxSnapNotes(Selection s)
Chris@239 2621 {
matthiasm@274 2622 cerr << "in auxSnapNotes" << endl;
Chris@239 2623 FlexiNoteLayer *layer =
Chris@239 2624 qobject_cast<FlexiNoteLayer *>(m_analyser->getLayer(Analyser::Notes));
Chris@239 2625 if (!layer) return;
Chris@239 2626
Chris@239 2627 layer->snapSelectedNotesToPitchTrack(m_analyser->getPane(), s);
Chris@239 2628 }
Chris@239 2629
Chris@239 2630 void
Chris@240 2631 MainWindow::splitNote()
Chris@237 2632 {
Chris@237 2633 FlexiNoteLayer *layer =
Chris@237 2634 qobject_cast<FlexiNoteLayer *>(m_analyser->getLayer(Analyser::Notes));
Chris@237 2635 if (!layer) return;
Chris@237 2636
Chris@240 2637 layer->splitNotesAt(m_analyser->getPane(), m_viewManager->getPlaybackFrame());
Chris@237 2638 }
Chris@237 2639
Chris@237 2640 void
Chris@238 2641 MainWindow::mergeNotes()
Chris@238 2642 {
Chris@238 2643 FlexiNoteLayer *layer =
Chris@238 2644 qobject_cast<FlexiNoteLayer *>(m_analyser->getLayer(Analyser::Notes));
Chris@238 2645 if (!layer) return;
Chris@238 2646
Chris@238 2647 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@238 2648
Chris@238 2649 if (!selections.empty()) {
Chris@238 2650
Chris@238 2651 CommandHistory::getInstance()->startCompoundOperation
Chris@238 2652 (tr("Merge Notes"), true);
Chris@238 2653
Chris@238 2654 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@238 2655 k != selections.end(); ++k) {
Chris@240 2656 layer->mergeNotes(m_analyser->getPane(), *k, true);
Chris@238 2657 }
Chris@238 2658
Chris@238 2659 CommandHistory::getInstance()->endCompoundOperation();
Chris@238 2660 }
Chris@238 2661 }
Chris@238 2662
Chris@238 2663 void
matthiasm@292 2664 MainWindow::deleteNotes()
matthiasm@292 2665 {
matthiasm@292 2666 FlexiNoteLayer *layer =
matthiasm@292 2667 qobject_cast<FlexiNoteLayer *>(m_analyser->getLayer(Analyser::Notes));
matthiasm@292 2668 if (!layer) return;
matthiasm@292 2669
matthiasm@292 2670 MultiSelection::SelectionList selections = m_viewManager->getSelections();
matthiasm@292 2671
matthiasm@292 2672 if (!selections.empty()) {
matthiasm@292 2673
matthiasm@292 2674 CommandHistory::getInstance()->startCompoundOperation
matthiasm@292 2675 (tr("Delete Notes"), true);
matthiasm@292 2676
matthiasm@292 2677 for (MultiSelection::SelectionList::iterator k = selections.begin();
matthiasm@292 2678 k != selections.end(); ++k) {
matthiasm@294 2679 layer->deleteSelectionInclusive(*k);
matthiasm@292 2680 }
matthiasm@292 2681
matthiasm@292 2682 CommandHistory::getInstance()->endCompoundOperation();
matthiasm@292 2683 }
matthiasm@292 2684 }
matthiasm@292 2685
matthiasm@292 2686
matthiasm@292 2687 void
Chris@240 2688 MainWindow::formNoteFromSelection()
Chris@240 2689 {
Chris@571 2690 Pane *pane = m_analyser->getPane();
matthiasm@293 2691 Layer *layer0 = m_analyser->getLayer(Analyser::Notes);
Chris@573 2692 auto model = ModelById::getAs<NoteModel>(layer0->getModel());
Chris@571 2693 FlexiNoteLayer *layer = qobject_cast<FlexiNoteLayer *>(layer0);
Chris@573 2694 if (!layer || !model) return;
Chris@240 2695
Chris@240 2696 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@240 2697
Chris@240 2698 if (!selections.empty()) {
Chris@240 2699
Chris@240 2700 CommandHistory::getInstance()->startCompoundOperation
Chris@240 2701 (tr("Form Note from Selection"), true);
Chris@571 2702
Chris@240 2703 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@240 2704 k != selections.end(); ++k) {
Chris@571 2705
Chris@571 2706 // Chop existing events at start and end frames; remember
Chris@571 2707 // the first starting pitch, to use as default for new
Chris@571 2708 // note; delete existing events; create new note; ask
Chris@571 2709 // layer to merge, just in order to adapt the note to the
Chris@571 2710 // existing pitch track if possible. This way we should
Chris@571 2711 // handle all the possible cases of existing notes that
Chris@571 2712 // may or may not overlap the start or end times
Chris@571 2713
Chris@571 2714 sv_frame_t start = k->getStartFrame();
Chris@571 2715 sv_frame_t end = k->getEndFrame();
Chris@571 2716
Chris@571 2717 EventVector existing =
Chris@571 2718 model->getEventsStartingWithin(start, end - start);
Chris@571 2719
Chris@571 2720 int defaultPitch = 100;
Chris@571 2721 if (!existing.empty()) {
Chris@571 2722 defaultPitch = int(roundf(existing.begin()->getValue()));
matthiasm@293 2723 }
Chris@571 2724
Chris@571 2725 layer->splitNotesAt(pane, start);
Chris@571 2726 layer->splitNotesAt(pane, end);
Chris@571 2727 layer->deleteSelection(*k);
Chris@571 2728
Chris@571 2729 layer->addNoteOn(start, defaultPitch, 100);
Chris@571 2730 layer->addNoteOff(end, defaultPitch);
Chris@571 2731
Chris@571 2732 layer->mergeNotes(pane, *k, false);
Chris@240 2733 }
Chris@240 2734
matthiasm@293 2735 CommandHistory::getInstance()->endCompoundOperation();
Chris@240 2736 }
Chris@240 2737 }
Chris@240 2738
Chris@240 2739 void
Chris@0 2740 MainWindow::playSpeedChanged(int position)
Chris@0 2741 {
Chris@474 2742 PlaySpeedRangeMapper mapper;
Chris@0 2743
Chris@399 2744 double percent = m_playSpeed->mappedValue();
Chris@399 2745 double factor = mapper.getFactorForValue(percent);
Chris@0 2746
Chris@474 2747 int centre = m_playSpeed->defaultValue();
Chris@474 2748
Chris@474 2749 // Percentage is shown to 0dp if >100, to 1dp if <100; factor is
Chris@474 2750 // shown to 3sf
Chris@474 2751
Chris@474 2752 char pcbuf[30];
Chris@474 2753 char facbuf[30];
Chris@474 2754
Chris@474 2755 if (position == centre) {
Chris@0 2756 contextHelpChanged(tr("Playback speed: Normal"));
Chris@474 2757 } else if (position < centre) {
Chris@474 2758 sprintf(pcbuf, "%.1f", percent);
Chris@474 2759 sprintf(facbuf, "%.3g", 1.0 / factor);
Chris@474 2760 contextHelpChanged(tr("Playback speed: %1% (%2x slower)")
Chris@474 2761 .arg(pcbuf)
Chris@474 2762 .arg(facbuf));
Chris@0 2763 } else {
Chris@474 2764 sprintf(pcbuf, "%.0f", percent);
Chris@474 2765 sprintf(facbuf, "%.3g", factor);
Chris@474 2766 contextHelpChanged(tr("Playback speed: %1% (%2x faster)")
Chris@474 2767 .arg(pcbuf)
Chris@474 2768 .arg(facbuf));
Chris@0 2769 }
Chris@0 2770
Chris@474 2771 m_playSource->setTimeStretch(1.0 / factor); // factor is a speedup
Chris@0 2772
Chris@0 2773 updateMenuStates();
Chris@0 2774 }
Chris@0 2775
Chris@0 2776 void
Chris@0 2777 MainWindow::playSharpenToggled()
Chris@0 2778 {
Chris@0 2779 QSettings settings;
Chris@0 2780 settings.beginGroup("MainWindow");
Chris@0 2781 settings.setValue("playsharpen", m_playSharpen->isChecked());
Chris@0 2782 settings.endGroup();
Chris@0 2783
Chris@0 2784 playSpeedChanged(m_playSpeed->value());
justin@157 2785 // TODO: pitch gain?
Chris@0 2786 }
Chris@0 2787
Chris@0 2788 void
Chris@0 2789 MainWindow::playMonoToggled()
Chris@0 2790 {
Chris@0 2791 QSettings settings;
Chris@0 2792 settings.beginGroup("MainWindow");
Chris@0 2793 settings.setValue("playmono", m_playMono->isChecked());
Chris@0 2794 settings.endGroup();
Chris@0 2795
Chris@0 2796 playSpeedChanged(m_playSpeed->value());
justin@157 2797 // TODO: pitch gain?
Chris@0 2798 }
Chris@0 2799
Chris@0 2800 void
Chris@0 2801 MainWindow::speedUpPlayback()
Chris@0 2802 {
Chris@0 2803 int value = m_playSpeed->value();
Chris@0 2804 value = value + m_playSpeed->pageStep();
Chris@0 2805 if (value > m_playSpeed->maximum()) value = m_playSpeed->maximum();
Chris@0 2806 m_playSpeed->setValue(value);
Chris@0 2807 }
Chris@0 2808
Chris@0 2809 void
Chris@0 2810 MainWindow::slowDownPlayback()
Chris@0 2811 {
Chris@0 2812 int value = m_playSpeed->value();
Chris@0 2813 value = value - m_playSpeed->pageStep();
Chris@0 2814 if (value < m_playSpeed->minimum()) value = m_playSpeed->minimum();
Chris@0 2815 m_playSpeed->setValue(value);
Chris@0 2816 }
Chris@0 2817
Chris@0 2818 void
Chris@0 2819 MainWindow::restoreNormalPlayback()
Chris@0 2820 {
Chris@0 2821 m_playSpeed->setValue(m_playSpeed->defaultValue());
Chris@0 2822 }
Chris@0 2823
justin@157 2824 void
Chris@404 2825 MainWindow::audioGainChanged(float gain)
justin@159 2826 {
Chris@404 2827 double db = AudioLevel::multiplier_to_dB(gain);
Chris@404 2828 cerr << "gain = " << gain << " (" << db << " dB)" << endl;
Chris@404 2829 contextHelpChanged(tr("Audio Gain: %1 dB").arg(db));
Chris@417 2830 if (gain == 0.f) {
Chris@417 2831 m_analyser->setAudible(Analyser::Audio, false);
Chris@417 2832 } else {
Chris@417 2833 m_analyser->setAudible(Analyser::Audio, true);
Chris@417 2834 m_analyser->setGain(Analyser::Audio, gain);
Chris@417 2835 }
justin@159 2836 updateMenuStates();
justin@159 2837 }
justin@159 2838
justin@159 2839 void
Chris@404 2840 MainWindow::pitchGainChanged(float gain)
justin@159 2841 {
Chris@404 2842 double db = AudioLevel::multiplier_to_dB(gain);
Chris@404 2843 cerr << "gain = " << gain << " (" << db << " dB)" << endl;
Chris@404 2844 contextHelpChanged(tr("Pitch Gain: %1 dB").arg(db));
Chris@417 2845 if (gain == 0.f) {
Chris@417 2846 m_analyser->setAudible(Analyser::PitchTrack, false);
Chris@417 2847 } else {
Chris@417 2848 m_analyser->setAudible(Analyser::PitchTrack, true);
Chris@417 2849 m_analyser->setGain(Analyser::PitchTrack, gain);
Chris@417 2850 }
justin@157 2851 updateMenuStates();
justin@157 2852 }
justin@157 2853
justin@157 2854 void
Chris@404 2855 MainWindow::notesGainChanged(float gain)
justin@157 2856 {
Chris@404 2857 double db = AudioLevel::multiplier_to_dB(gain);
Chris@404 2858 cerr << "gain = " << gain << " (" << db << " dB)" << endl;
Chris@404 2859 contextHelpChanged(tr("Notes Gain: %1 dB").arg(db));
Chris@417 2860 if (gain == 0.f) {
Chris@417 2861 m_analyser->setAudible(Analyser::Notes, false);
Chris@417 2862 } else {
Chris@417 2863 m_analyser->setAudible(Analyser::Notes, true);
Chris@417 2864 m_analyser->setGain(Analyser::Notes, gain);
Chris@417 2865 }
justin@159 2866 updateMenuStates();
justin@159 2867 }
justin@159 2868
justin@159 2869 void
Chris@404 2870 MainWindow::audioPanChanged(float pan)
justin@159 2871 {
Chris@404 2872 contextHelpChanged(tr("Audio Pan: %1").arg(pan));
Chris@404 2873 m_analyser->setPan(Analyser::Audio, pan);
justin@160 2874 updateMenuStates();
justin@160 2875 }
justin@160 2876
justin@160 2877 void
Chris@404 2878 MainWindow::pitchPanChanged(float pan)
justin@160 2879 {
Chris@404 2880 contextHelpChanged(tr("Pitch Pan: %1").arg(pan));
Chris@404 2881 m_analyser->setPan(Analyser::PitchTrack, pan);
justin@160 2882 updateMenuStates();
justin@160 2883 }
justin@160 2884
justin@160 2885 void
Chris@404 2886 MainWindow::notesPanChanged(float pan)
justin@160 2887 {
Chris@404 2888 contextHelpChanged(tr("Notes Pan: %1").arg(pan));
Chris@404 2889 m_analyser->setPan(Analyser::Notes, pan);
justin@160 2890 updateMenuStates();
justin@160 2891 }
justin@160 2892
justin@160 2893 void
Chris@0 2894 MainWindow::updateVisibleRangeDisplay(Pane *p) const
Chris@0 2895 {
Chris@0 2896 if (!getMainModel() || !p) {
Chris@0 2897 return;
Chris@0 2898 }
Chris@0 2899
Chris@0 2900 bool haveSelection = false;
Chris@403 2901 sv_frame_t startFrame = 0, endFrame = 0;
Chris@0 2902
Chris@0 2903 if (m_viewManager && m_viewManager->haveInProgressSelection()) {
Chris@0 2904
Chris@0 2905 bool exclusive = false;
Chris@0 2906 Selection s = m_viewManager->getInProgressSelection(exclusive);
Chris@0 2907
Chris@0 2908 if (!s.isEmpty()) {
Chris@0 2909 haveSelection = true;
Chris@0 2910 startFrame = s.getStartFrame();
Chris@0 2911 endFrame = s.getEndFrame();
Chris@0 2912 }
Chris@0 2913 }
Chris@0 2914
Chris@0 2915 if (!haveSelection) {
Chris@0 2916 startFrame = p->getFirstVisibleFrame();
Chris@0 2917 endFrame = p->getLastVisibleFrame();
Chris@0 2918 }
Chris@0 2919
Chris@0 2920 RealTime start = RealTime::frame2RealTime
Chris@0 2921 (startFrame, getMainModel()->getSampleRate());
Chris@0 2922
Chris@0 2923 RealTime end = RealTime::frame2RealTime
Chris@0 2924 (endFrame, getMainModel()->getSampleRate());
Chris@0 2925
Chris@0 2926 RealTime duration = end - start;
Chris@0 2927
Chris@0 2928 QString startStr, endStr, durationStr;
Chris@0 2929 startStr = start.toText(true).c_str();
Chris@0 2930 endStr = end.toText(true).c_str();
Chris@0 2931 durationStr = duration.toText(true).c_str();
Chris@0 2932
Chris@0 2933 if (haveSelection) {
Chris@0 2934 m_myStatusMessage = tr("Selection: %1 to %2 (duration %3)")
Chris@0 2935 .arg(startStr).arg(endStr).arg(durationStr);
Chris@0 2936 } else {
Chris@0 2937 m_myStatusMessage = tr("Visible: %1 to %2 (duration %3)")
Chris@0 2938 .arg(startStr).arg(endStr).arg(durationStr);
Chris@0 2939 }
matthiasm@42 2940
Chris@362 2941 getStatusLabel()->setText(m_myStatusMessage);
Chris@0 2942 }
Chris@0 2943
Chris@0 2944 void
Chris@0 2945 MainWindow::updatePositionStatusDisplays() const
Chris@0 2946 {
Chris@0 2947 if (!statusBar()->isVisible()) return;
Chris@0 2948
Chris@0 2949 }
Chris@0 2950
Chris@0 2951 void
Chris@521 2952 MainWindow::monitoringLevelsChanged(float left, float right)
Chris@0 2953 {
Chris@0 2954 m_fader->setPeakLeft(left);
Chris@0 2955 m_fader->setPeakRight(right);
Chris@0 2956 }
Chris@0 2957
Chris@0 2958 void
Chris@399 2959 MainWindow::sampleRateMismatch(sv_samplerate_t ,
Chris@399 2960 sv_samplerate_t ,
Chris@399 2961 bool )
Chris@0 2962 {
Chris@0 2963 updateDescriptionLabel();
Chris@0 2964 }
Chris@0 2965
Chris@0 2966 void
Chris@0 2967 MainWindow::audioOverloadPluginDisabled()
Chris@0 2968 {
Chris@0 2969 QMessageBox::information
Chris@0 2970 (this, tr("Audio processing overload"),
Chris@0 2971 tr("<b>Overloaded</b><p>Audio effects plugin auditioning has been disabled due to a processing overload."));
Chris@0 2972 }
Chris@0 2973
Chris@0 2974 void
Chris@0 2975 MainWindow::layerRemoved(Layer *layer)
Chris@0 2976 {
Chris@0 2977 MainWindowBase::layerRemoved(layer);
Chris@0 2978 }
Chris@0 2979
Chris@0 2980 void
Chris@0 2981 MainWindow::layerInAView(Layer *layer, bool inAView)
Chris@0 2982 {
Chris@0 2983 MainWindowBase::layerInAView(layer, inAView);
Chris@0 2984 }
Chris@0 2985
Chris@0 2986 void
Chris@573 2987 MainWindow::modelAdded(ModelId model)
Chris@0 2988 {
Chris@0 2989 MainWindowBase::modelAdded(model);
Chris@573 2990 auto dtvm = ModelById::getAs<DenseTimeValueModel>(model);
Chris@0 2991 if (dtvm) {
Chris@70 2992 cerr << "A dense time-value model (such as an audio file) has been loaded" << endl;
Chris@0 2993 }
Chris@0 2994 }
Chris@0 2995
Chris@0 2996 void
Chris@573 2997 MainWindow::mainModelChanged(ModelId model)
Chris@0 2998 {
Chris@0 2999 m_panLayer->setModel(model);
Chris@0 3000
Chris@0 3001 MainWindowBase::mainModelChanged(model);
Chris@0 3002
Chris@479 3003 if (m_playTarget || m_audioIO) {
Chris@0 3004 connect(m_fader, SIGNAL(valueChanged(float)),
Chris@474 3005 this, SLOT(mainModelGainChanged(float)));
Chris@474 3006 }
Chris@474 3007 }
Chris@474 3008
Chris@474 3009 void
Chris@474 3010 MainWindow::mainModelGainChanged(float gain)
Chris@474 3011 {
Chris@474 3012 if (m_playTarget) {
Chris@474 3013 m_playTarget->setOutputGain(gain);
Chris@479 3014 } else if (m_audioIO) {
Chris@479 3015 m_audioIO->setOutputGain(gain);
Chris@0 3016 }
Chris@259 3017 }
Chris@259 3018
Chris@259 3019 void
Chris@323 3020 MainWindow::analyseNow()
Chris@323 3021 {
Chris@323 3022 cerr << "analyseNow called" << endl;
Chris@325 3023 if (!m_analyser) return;
Chris@325 3024
Chris@325 3025 CommandHistory::getInstance()->startCompoundOperation
Chris@325 3026 (tr("Analyse Audio"), true);
Chris@325 3027
Chris@325 3028 QString error = m_analyser->analyseExistingFile();
Chris@325 3029
Chris@325 3030 CommandHistory::getInstance()->endCompoundOperation();
Chris@325 3031
Chris@325 3032 if (error != "") {
Chris@325 3033 QMessageBox::warning
Chris@325 3034 (this,
Chris@325 3035 tr("Failed to analyse audio"),
Chris@325 3036 tr("<b>Analysis failed</b><p>%1</p>").arg(error),
Chris@325 3037 QMessageBox::Ok);
Chris@325 3038 }
Chris@323 3039 }
Chris@323 3040
Chris@323 3041 void
Chris@259 3042 MainWindow::analyseNewMainModel()
Chris@259 3043 {
Chris@573 3044 auto model = getMainModel();
Chris@260 3045
Chris@604 3046 SVDEBUG << "MainWindow::analyseNewMainModel: main model is " << model << endl;
Chris@604 3047
Chris@604 3048 SVDEBUG << "(document is " << m_document << ", it says main model is " << m_document->getMainModel() << ")" << endl;
Chris@259 3049
Chris@324 3050 if (!model) {
Chris@324 3051 cerr << "no main model!" << endl;
Chris@324 3052 return;
Chris@324 3053 }
Chris@324 3054
Chris@324 3055 if (!m_paneStack) {
Chris@324 3056 cerr << "no pane stack!" << endl;
Chris@324 3057 return;
Chris@324 3058 }
Chris@324 3059
Chris@324 3060 int pc = m_paneStack->getPaneCount();
Chris@324 3061 Pane *pane = 0;
Chris@324 3062 Pane *selectionStrip = 0;
Chris@324 3063
Chris@324 3064 if (pc < 2) {
Chris@604 3065 SVDEBUG << "MainWindow::analyseNewMainModel: Adding pane and selection strip (ruler)" << endl;
Chris@604 3066 pane = m_paneStack->addPane();
Chris@604 3067 selectionStrip = m_paneStack->addPane();
Chris@324 3068 m_document->addLayerToView
Chris@324 3069 (selectionStrip,
Chris@324 3070 m_document->createMainModelLayer(LayerFactory::TimeRuler));
Chris@324 3071 } else {
Chris@324 3072 pane = m_paneStack->getPane(0);
Chris@324 3073 selectionStrip = m_paneStack->getPane(1);
Chris@324 3074 }
Chris@324 3075
Chris@362 3076 pane->setPlaybackFollow(PlaybackScrollPage);
Chris@362 3077
Chris@324 3078 if (selectionStrip) {
Chris@362 3079 selectionStrip->setPlaybackFollow(PlaybackScrollPage);
Chris@324 3080 selectionStrip->setFixedHeight(26);
Chris@324 3081 m_paneStack->sizePanesEqually();
Chris@324 3082 m_viewManager->clearToolModeOverrides();
Chris@324 3083 m_viewManager->setToolModeFor(selectionStrip,
Chris@324 3084 ViewManager::SelectMode);
Chris@324 3085 }
Chris@324 3086
Chris@324 3087 if (pane) {
Chris@324 3088
Chris@324 3089 disconnect(pane, SIGNAL(regionOutlined(QRect)),
Chris@324 3090 pane, SLOT(zoomToRegion(QRect)));
Chris@324 3091 connect(pane, SIGNAL(regionOutlined(QRect)),
Chris@324 3092 this, SLOT(regionOutlined(QRect)));
Chris@324 3093
Chris@324 3094 QString error = m_analyser->newFileLoaded
Chris@573 3095 (m_document, getMainModelId(), m_paneStack, pane);
Chris@324 3096 if (error != "") {
Chris@324 3097 QMessageBox::warning
Chris@324 3098 (this,
Chris@324 3099 tr("Failed to analyse audio"),
Chris@324 3100 tr("<b>Analysis failed</b><p>%1</p>").arg(error),
Chris@324 3101 QMessageBox::Ok);
Chris@72 3102 }
Chris@72 3103 }
matthiasm@366 3104
Chris@573 3105 if (!m_withSpectrogram) {
matthiasm@366 3106 m_analyser->setVisible(Analyser::Spectrogram, false);
matthiasm@366 3107 }
matthiasm@366 3108
Chris@573 3109 if (!m_withSonification) {
matthiasm@366 3110 m_analyser->setAudible(Analyser::PitchTrack, false);
matthiasm@366 3111 m_analyser->setAudible(Analyser::Notes, false);
matthiasm@366 3112 }
Chris@419 3113
Chris@419 3114 updateLayerStatuses();
Chris@487 3115 documentRestored();
Chris@0 3116 }
Chris@0 3117
Chris@0 3118 void
Chris@0 3119 MainWindow::modelGenerationFailed(QString transformName, QString message)
Chris@0 3120 {
Chris@0 3121 if (message != "") {
Chris@0 3122
Chris@0 3123 QMessageBox::warning
Chris@0 3124 (this,
Chris@0 3125 tr("Failed to generate layer"),
Chris@0 3126 tr("<b>Layer generation failed</b><p>Failed to generate derived layer.<p>The layer transform \"%1\" failed:<p>%2")
Chris@0 3127 .arg(transformName).arg(message),
Chris@0 3128 QMessageBox::Ok);
Chris@0 3129 } else {
Chris@0 3130 QMessageBox::warning
Chris@0 3131 (this,
Chris@0 3132 tr("Failed to generate layer"),
Chris@0 3133 tr("<b>Layer generation failed</b><p>Failed to generate a derived layer.<p>The layer transform \"%1\" failed.<p>No error information is available.")
Chris@0 3134 .arg(transformName),
Chris@0 3135 QMessageBox::Ok);
Chris@0 3136 }
Chris@0 3137 }
Chris@0 3138
Chris@0 3139 void
Chris@355 3140 MainWindow::modelGenerationWarning(QString /* transformName */, QString message)
Chris@0 3141 {
Chris@0 3142 QMessageBox::warning
Chris@0 3143 (this, tr("Warning"), message, QMessageBox::Ok);
Chris@0 3144 }
Chris@0 3145
Chris@0 3146 void
Chris@0 3147 MainWindow::modelRegenerationFailed(QString layerName,
Chris@399 3148 QString transformName,
Chris@399 3149 QString message)
Chris@0 3150 {
Chris@0 3151 if (message != "") {
Chris@0 3152
Chris@0 3153 QMessageBox::warning
Chris@0 3154 (this,
Chris@0 3155 tr("Failed to regenerate layer"),
Chris@0 3156 tr("<b>Layer generation failed</b><p>Failed to regenerate derived layer \"%1\" using new data model as input.<p>The layer transform \"%2\" failed:<p>%3")
Chris@0 3157 .arg(layerName).arg(transformName).arg(message),
Chris@0 3158 QMessageBox::Ok);
Chris@0 3159 } else {
Chris@0 3160 QMessageBox::warning
Chris@0 3161 (this,
Chris@0 3162 tr("Failed to regenerate layer"),
Chris@0 3163 tr("<b>Layer generation failed</b><p>Failed to regenerate derived layer \"%1\" using new data model as input.<p>The layer transform \"%2\" failed.<p>No error information is available.")
Chris@0 3164 .arg(layerName).arg(transformName),
Chris@0 3165 QMessageBox::Ok);
Chris@0 3166 }
Chris@0 3167 }
Chris@0 3168
Chris@0 3169 void
Chris@0 3170 MainWindow::modelRegenerationWarning(QString layerName,
Chris@355 3171 QString /* transformName */,
Chris@355 3172 QString message)
Chris@0 3173 {
Chris@0 3174 QMessageBox::warning
Chris@0 3175 (this, tr("Warning"), tr("<b>Warning when regenerating layer</b><p>When regenerating the derived layer \"%1\" using new data model as input:<p>%2").arg(layerName).arg(message), QMessageBox::Ok);
Chris@0 3176 }
Chris@0 3177
Chris@0 3178 void
Chris@697 3179 MainWindow::alignmentFailed(ModelId, QString message)
Chris@0 3180 {
Chris@0 3181 QMessageBox::warning
Chris@0 3182 (this,
Chris@0 3183 tr("Failed to calculate alignment"),
Chris@520 3184 tr("<b>Alignment calculation failed</b><p>Failed to calculate an audio alignment:<p>%1")
Chris@520 3185 .arg(message),
Chris@0 3186 QMessageBox::Ok);
Chris@0 3187 }
Chris@0 3188
Chris@0 3189 void
Chris@688 3190 MainWindow::paneRightButtonMenuRequested(Pane *pane, QPoint position)
Chris@0 3191 {
Chris@70 3192 // cerr << "MainWindow::rightButtonMenuRequested(" << pane << ", " << position.x() << ", " << position.y() << ")" << endl;
Chris@0 3193 m_paneStack->setCurrentPane(pane);
Chris@0 3194 m_rightButtonMenu->popup(position);
Chris@0 3195 }
Chris@0 3196
Chris@0 3197 void
Chris@688 3198 MainWindow::panePropertiesRightButtonMenuRequested(Pane *, QPoint)
Chris@688 3199 {
Chris@688 3200 }
Chris@688 3201
Chris@688 3202 void
Chris@688 3203 MainWindow::layerPropertiesRightButtonMenuRequested(Pane *, Layer *, QPoint)
Chris@688 3204 {
Chris@688 3205 }
Chris@688 3206
Chris@688 3207 void
Chris@355 3208 MainWindow::handleOSCMessage(const OSCMessage &)
Chris@0 3209 {
Chris@70 3210 cerr << "MainWindow::handleOSCMessage: Not implemented" << endl;
Chris@0 3211 }
Chris@0 3212
Chris@0 3213 void
Chris@0 3214 MainWindow::mouseEnteredWidget()
Chris@0 3215 {
Chris@3 3216 QWidget *w = qobject_cast<QWidget *>(sender());
Chris@0 3217 if (!w) return;
Chris@0 3218
Chris@0 3219 if (w == m_fader) {
Chris@0 3220 contextHelpChanged(tr("Adjust the master playback level"));
Chris@0 3221 } else if (w == m_playSpeed) {
Chris@0 3222 contextHelpChanged(tr("Adjust the master playback speed"));
Chris@0 3223 } else if (w == m_playSharpen && w->isEnabled()) {
Chris@0 3224 contextHelpChanged(tr("Toggle transient sharpening for playback time scaling"));
Chris@0 3225 } else if (w == m_playMono && w->isEnabled()) {
Chris@0 3226 contextHelpChanged(tr("Toggle mono mode for playback time scaling"));
Chris@0 3227 }
Chris@0 3228 }
Chris@0 3229
Chris@0 3230 void
Chris@0 3231 MainWindow::mouseLeftWidget()
Chris@0 3232 {
Chris@0 3233 contextHelpChanged("");
Chris@0 3234 }
Chris@0 3235
Chris@0 3236 void
Chris@609 3237 MainWindow::betaReleaseWarning()
Chris@609 3238 {
Chris@609 3239 QMessageBox::information
Chris@609 3240 (this, tr("Beta release"),
Chris@609 3241 tr("<b>This is a beta release of %1</b><p>Please see the \"What's New\" option in the Help menu for a list of changes since the last proper release.</p>").arg(QApplication::applicationName()));
Chris@609 3242 }
Chris@609 3243
Chris@609 3244 void
Chris@0 3245 MainWindow::help()
Chris@0 3246 {
Chris@4 3247 //!!! todo: help URL!
matthiasm@369 3248 openHelpUrl(tr("http://code.soundsoftware.ac.uk/projects/tony/wiki/Reference"));
Chris@0 3249 }
Chris@0 3250
Chris@0 3251 void
Chris@585 3252 MainWindow::whatsNew()
Chris@585 3253 {
Chris@585 3254 QFile changelog(":CHANGELOG");
Chris@585 3255 changelog.open(QFile::ReadOnly);
Chris@585 3256 QByteArray content = changelog.readAll();
Chris@585 3257 QString text = QString::fromUtf8(content);
Chris@585 3258
Chris@585 3259 QDialog *d = new QDialog(this);
Chris@585 3260 d->setWindowTitle(tr("What's New"));
Chris@585 3261
Chris@585 3262 QGridLayout *layout = new QGridLayout;
Chris@585 3263 d->setLayout(layout);
Chris@585 3264
Chris@585 3265 int row = 0;
Chris@585 3266
Chris@585 3267 QLabel *iconLabel = new QLabel;
Chris@585 3268 iconLabel->setPixmap(QApplication::windowIcon().pixmap(64, 64));
Chris@585 3269 layout->addWidget(iconLabel, row, 0);
Chris@585 3270
Chris@585 3271 layout->addWidget
Chris@585 3272 (new QLabel(tr("<h3>What's New in %1</h3>")
Chris@585 3273 .arg(QApplication::applicationName())),
Chris@585 3274 row++, 1);
Chris@585 3275 layout->setColumnStretch(2, 10);
Chris@585 3276
Chris@585 3277 QTextEdit *textEdit = new QTextEdit;
Chris@585 3278 layout->addWidget(textEdit, row++, 1, 1, 2);
Chris@585 3279
Chris@585 3280 if (m_newerVersionIs != "") {
Chris@585 3281 layout->addWidget(new QLabel(tr("<b>Note:</b> A newer version of %1 is available.<br>(Version %2 is available; you are using version %3)").arg(QApplication::applicationName()).arg(m_newerVersionIs).arg(TONY_VERSION)), row++, 1, 1, 2);
Chris@585 3282 }
Chris@585 3283
Chris@585 3284 QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Ok);
Chris@585 3285 layout->addWidget(bb, row++, 0, 1, 3);
Chris@585 3286 connect(bb, SIGNAL(accepted()), d, SLOT(accept()));
Chris@585 3287
Chris@585 3288 text.replace('\r', "");
Chris@585 3289 text.replace(QRegExp("(.)\n +(.)"), "\\1 \\2");
Chris@585 3290 text.replace(QRegExp("\n - ([^\n]+)"), "\n<li>\\1</li>");
Chris@585 3291 text.replace(QRegExp(": *\n"), ":\n<ul>\n");
Chris@585 3292 text.replace(QRegExp("</li>\n\\s*\n"), "</li>\n</ul>\n\n");
Chris@585 3293 text.replace(QRegExp("\n(\\w[^:\n]+:)"), "\n<p><b>\\1</b></p>");
Chris@585 3294 // text.replace(QRegExp("<li>([^,.\n]+)([,.] +\\w)"), "<li><b>\\1</b>\\2");
Chris@585 3295
Chris@585 3296 textEdit->setHtml(text);
Chris@585 3297 textEdit->setReadOnly(true);
Chris@585 3298
Chris@585 3299 d->setMinimumSize(m_viewManager->scalePixelSize(520),
Chris@585 3300 m_viewManager->scalePixelSize(450));
Chris@585 3301
Chris@585 3302 d->exec();
Chris@585 3303
Chris@585 3304 delete d;
Chris@585 3305 }
Chris@585 3306
Chris@598 3307 QString
Chris@598 3308 MainWindow::getReleaseText() const
Chris@598 3309 {
Chris@598 3310 bool debug = false;
Chris@598 3311 QString version = "(unknown version)";
Chris@598 3312
Chris@598 3313 #ifdef BUILD_DEBUG
Chris@598 3314 debug = true;
Chris@598 3315 #endif // BUILD_DEBUG
Chris@598 3316 #ifdef TONY_VERSION
Chris@598 3317 #ifdef SVNREV
Chris@598 3318 version = tr("Release %1 : Revision %2").arg(TONY_VERSION).arg(SVNREV);
Chris@598 3319 #else // !SVNREV
Chris@598 3320 version = tr("Release %1").arg(TONY_VERSION);
Chris@598 3321 #endif // SVNREV
Chris@598 3322 #else // !TONY_VERSION
Chris@598 3323 #ifdef SVNREV
Chris@598 3324 version = tr("Unreleased : Revision %1").arg(SVNREV);
Chris@598 3325 #endif // SVNREV
Chris@598 3326 #endif // TONY_VERSION
Chris@598 3327
Chris@598 3328 return tr("%1 : %2 configuration, %3-bit build")
Chris@598 3329 .arg(version)
Chris@598 3330 .arg(debug ? tr("Debug") : tr("Release"))
Chris@598 3331 .arg(sizeof(void *) * 8);
Chris@598 3332 }
Chris@598 3333
Chris@585 3334 void
Chris@0 3335 MainWindow::about()
Chris@0 3336 {
Chris@0 3337 QString aboutText;
Chris@0 3338
Chris@0 3339 aboutText += tr("<h3>About Tony</h3>");
Chris@0 3340 aboutText += tr("<p>Tony is a program for interactive note and pitch analysis and annotation.</p>");
Chris@598 3341 aboutText += QString("<p><small>%1</small></p>").arg(getReleaseText());
Chris@212 3342 aboutText += tr("<p>Using Qt framework version %1.</p>")
Chris@206 3343 .arg(QT_VERSION_STR);
Chris@0 3344
Chris@0 3345 aboutText +=
Chris@585 3346 "<p>Copyright &copy; 2005&ndash;2019 Chris Cannam, Queen Mary University of London, and the Tony project authors: Matthias Mauch, George Fazekas, Justin Salamon, and Rachel Bittner.</p>"
Chris@212 3347 "<p>pYIN analysis plugin written by Matthias Mauch.</p>"
Chris@90 3348 "<p>This program is free software; you can redistribute it and/or "
Chris@90 3349 "modify it under the terms of the GNU General Public License as "
Chris@90 3350 "published by the Free Software Foundation; either version 2 of the "
Chris@0 3351 "License, or (at your option) any later version.<br>See the file "
Chris@0 3352 "COPYING included with this distribution for more information.</p>";
Chris@0 3353
Chris@585 3354 // use our own dialog so we can influence the size
Chris@585 3355
Chris@585 3356 QDialog *d = new QDialog(this);
Chris@585 3357
Chris@585 3358 d->setWindowTitle(tr("About %1").arg(QApplication::applicationName()));
Chris@585 3359
Chris@585 3360 QGridLayout *layout = new QGridLayout;
Chris@585 3361 d->setLayout(layout);
Chris@585 3362
Chris@585 3363 int row = 0;
Chris@585 3364
Chris@585 3365 QLabel *iconLabel = new QLabel;
Chris@585 3366 iconLabel->setPixmap(QApplication::windowIcon().pixmap(64, 64));
Chris@585 3367 layout->addWidget(iconLabel, row, 0, Qt::AlignTop);
Chris@585 3368
Chris@585 3369 QLabel *mainText = new QLabel();
Chris@585 3370 layout->addWidget(mainText, row, 1, 1, 2);
Chris@585 3371
Chris@585 3372 layout->setRowStretch(row, 10);
Chris@585 3373 layout->setColumnStretch(1, 10);
Chris@585 3374
Chris@585 3375 ++row;
Chris@585 3376
Chris@585 3377 QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Ok);
Chris@585 3378 layout->addWidget(bb, row++, 0, 1, 3);
Chris@585 3379 connect(bb, SIGNAL(accepted()), d, SLOT(accept()));
Chris@585 3380
Chris@585 3381 mainText->setWordWrap(true);
Chris@585 3382 mainText->setOpenExternalLinks(true);
Chris@585 3383 mainText->setText(aboutText);
Chris@585 3384
Chris@585 3385 d->setMinimumSize(m_viewManager->scalePixelSize(420),
Chris@585 3386 m_viewManager->scalePixelSize(200));
Chris@585 3387
Chris@585 3388 d->exec();
Chris@585 3389
Chris@585 3390 delete d;
Chris@0 3391 }
Chris@0 3392
Chris@0 3393 void
Chris@0 3394 MainWindow::keyReference()
Chris@0 3395 {
Chris@0 3396 m_keyReference->show();
Chris@0 3397 }
Chris@0 3398
Chris@231 3399 void
Chris@231 3400 MainWindow::newerVersionAvailable(QString version)
Chris@231 3401 {
Chris@585 3402 m_newerVersionIs = version;
Chris@585 3403
Chris@231 3404 //!!! nicer URL would be nicer
Chris@231 3405 QSettings settings;
Chris@231 3406 settings.beginGroup("NewerVersionWarning");
Chris@231 3407 QString tag = QString("version-%1-available-show").arg(version);
Chris@231 3408 if (settings.value(tag, true).toBool()) {
Chris@231 3409 QString title(tr("Newer version available"));
Chris@231 3410 QString text(tr("<h3>Newer version available</h3><p>You are using version %1 of Tony, but version %2 is now available.</p><p>Please see the <a href=\"http://code.soundsoftware.ac.uk/projects/tony/\">Tony website</a> for more information.</p>").arg(TONY_VERSION).arg(version));
Chris@231 3411 QMessageBox::information(this, title, text);
Chris@231 3412 settings.setValue(tag, false);
Chris@231 3413 }
Chris@231 3414 settings.endGroup();
Chris@231 3415 }
Chris@231 3416
matthiasm@356 3417 void
matthiasm@356 3418 MainWindow::ffwd()
matthiasm@356 3419 {
matthiasm@356 3420 if (!getMainModel()) return;
matthiasm@356 3421
Chris@403 3422 sv_frame_t frame = m_viewManager->getPlaybackFrame();
matthiasm@356 3423 ++frame;
matthiasm@356 3424
Chris@403 3425 sv_samplerate_t sr = getMainModel()->getSampleRate();
matthiasm@356 3426
matthiasm@356 3427 // The step is supposed to scale and be as wide as a step of
matthiasm@357 3428 // m_defaultFfwdRwdStep seconds at zoom level 720 and sr = 44100
Chris@540 3429
Chris@540 3430 ZoomLevel zoom = m_viewManager->getGlobalZoom();
Chris@540 3431 double framesPerPixel = 1.0;
Chris@540 3432 if (zoom.zone == ZoomLevel::FramesPerPixel) {
Chris@540 3433 framesPerPixel = zoom.level;
Chris@540 3434 } else {
Chris@540 3435 framesPerPixel = 1.0 / zoom.level;
Chris@540 3436 }
Chris@540 3437 double defaultFramesPerPixel = (720 * 44100) / sr;
Chris@540 3438 double scaler = framesPerPixel / defaultFramesPerPixel;
Chris@403 3439 RealTime step = m_defaultFfwdRwdStep * scaler;
Chris@403 3440
matthiasm@356 3441 frame = RealTime::realTime2Frame
Chris@403 3442 (RealTime::frame2RealTime(frame, sr) + step, sr);
Chris@403 3443
Chris@403 3444 if (frame > getMainModel()->getEndFrame()) {
matthiasm@356 3445 frame = getMainModel()->getEndFrame();
matthiasm@356 3446 }
Chris@403 3447
matthiasm@356 3448 if (frame < 0) frame = 0;
matthiasm@356 3449
matthiasm@356 3450 if (m_viewManager->getPlaySelectionMode()) {
Chris@403 3451 frame = m_viewManager->constrainFrameToSelection(frame);
matthiasm@356 3452 }
matthiasm@356 3453
matthiasm@356 3454 m_viewManager->setPlaybackFrame(frame);
matthiasm@356 3455
Chris@403 3456 if (frame == getMainModel()->getEndFrame() &&
matthiasm@356 3457 m_playSource &&
matthiasm@356 3458 m_playSource->isPlaying() &&
matthiasm@356 3459 !m_viewManager->getPlayLoopMode()) {
matthiasm@356 3460 stop();
matthiasm@356 3461 }
matthiasm@356 3462 }
matthiasm@356 3463
matthiasm@356 3464 void
matthiasm@356 3465 MainWindow::rewind()
matthiasm@356 3466 {
matthiasm@356 3467 if (!getMainModel()) return;
matthiasm@356 3468
Chris@403 3469 sv_frame_t frame = m_viewManager->getPlaybackFrame();
matthiasm@356 3470 if (frame > 0) --frame;
matthiasm@356 3471
Chris@403 3472 sv_samplerate_t sr = getMainModel()->getSampleRate();
matthiasm@356 3473
matthiasm@356 3474 // The step is supposed to scale and be as wide as a step of
matthiasm@357 3475 // m_defaultFfwdRwdStep seconds at zoom level 720 and sr = 44100
Chris@540 3476
Chris@540 3477 ZoomLevel zoom = m_viewManager->getGlobalZoom();
Chris@540 3478 double framesPerPixel = 1.0;
Chris@540 3479 if (zoom.zone == ZoomLevel::FramesPerPixel) {
Chris@540 3480 framesPerPixel = zoom.level;
Chris@540 3481 } else {
Chris@540 3482 framesPerPixel = 1.0 / zoom.level;
Chris@540 3483 }
Chris@540 3484 double defaultFramesPerPixel = (720 * 44100) / sr;
Chris@540 3485 double scaler = framesPerPixel / defaultFramesPerPixel;
Chris@403 3486 RealTime step = m_defaultFfwdRwdStep * scaler;
Chris@403 3487
matthiasm@356 3488 frame = RealTime::realTime2Frame
Chris@403 3489 (RealTime::frame2RealTime(frame, sr) - step, sr);
Chris@403 3490
Chris@403 3491 if (frame < getMainModel()->getStartFrame()) {
matthiasm@356 3492 frame = getMainModel()->getStartFrame();
matthiasm@356 3493 }
matthiasm@356 3494
matthiasm@356 3495 if (frame < 0) frame = 0;
matthiasm@356 3496
matthiasm@356 3497 if (m_viewManager->getPlaySelectionMode()) {
Chris@403 3498 frame = m_viewManager->constrainFrameToSelection(frame);
matthiasm@356 3499 }
matthiasm@356 3500
matthiasm@356 3501 m_viewManager->setPlaybackFrame(frame);
matthiasm@356 3502 }