annotate main/MainWindow.cpp @ 635:41e221cd740e

Update subrepos and adapt to new MainWindowBase API, ensuring that we don't get the recording permission dialog until we actually record
author Chris Cannam
date Wed, 16 Oct 2019 16:03:47 +0100
parents ef0778016c88
children e78f2c842a55
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 //!!! oh no, we need to update the menu states as well...
Chris@427 817 QSettings settings;
Chris@427 818 settings.beginGroup("Analyser");
Chris@427 819 settings.setValue("auto-analysis", true);
Chris@427 820 settings.setValue("precision-analysis", false);
Chris@427 821 settings.setValue("lowamp-analysis", true);
Chris@427 822 settings.setValue("onset-analysis", true);
Chris@427 823 settings.setValue("prune-analysis", true);
Chris@427 824 settings.endGroup();
Chris@427 825 updateAnalyseStates();
Chris@427 826 }
Chris@427 827
Chris@427 828 void
Chris@427 829 MainWindow::updateAnalyseStates()
Chris@427 830 {
Chris@323 831 QSettings settings;
Chris@323 832 settings.beginGroup("Analyser");
Chris@323 833 bool autoAnalyse = settings.value("auto-analysis", true).toBool();
Chris@323 834 bool precise = settings.value("precision-analysis", false).toBool();
matthiasm@345 835 bool lowamp = settings.value("lowamp-analysis", true).toBool();
matthiasm@423 836 bool onset = settings.value("onset-analysis", true).toBool();
matthiasm@423 837 bool prune = settings.value("prune-analysis", true).toBool();
Chris@323 838 settings.endGroup();
Chris@323 839
Chris@427 840 m_autoAnalyse->setChecked(autoAnalyse);
Chris@427 841 m_precise->setChecked(precise);
Chris@427 842 m_lowamp->setChecked(lowamp);
Chris@427 843 m_onset->setChecked(onset);
Chris@427 844 m_prune->setChecked(prune);
Chris@323 845 }
Chris@323 846
Chris@323 847 void
Chris@323 848 MainWindow::autoAnalysisToggled()
Chris@323 849 {
Chris@323 850 QAction *a = qobject_cast<QAction *>(sender());
Chris@323 851 if (!a) return;
Chris@323 852
Chris@323 853 bool set = a->isChecked();
Chris@323 854
Chris@323 855 QSettings settings;
Chris@323 856 settings.beginGroup("Analyser");
Chris@323 857 settings.setValue("auto-analysis", set);
Chris@323 858 settings.endGroup();
Chris@323 859 }
Chris@323 860
Chris@323 861 void
Chris@323 862 MainWindow::precisionAnalysisToggled()
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("precision-analysis", set);
Chris@323 872 settings.endGroup();
Chris@323 873
Chris@326 874 // don't run analyseNow() automatically -- it's a destructive operation
matthiasm@317 875 }
matthiasm@317 876
matthiasm@317 877 void
matthiasm@345 878 MainWindow::lowampAnalysisToggled()
matthiasm@345 879 {
matthiasm@345 880 QAction *a = qobject_cast<QAction *>(sender());
matthiasm@345 881 if (!a) return;
matthiasm@345 882
matthiasm@345 883 bool set = a->isChecked();
matthiasm@345 884
matthiasm@345 885 QSettings settings;
matthiasm@345 886 settings.beginGroup("Analyser");
matthiasm@345 887 settings.setValue("lowamp-analysis", set);
matthiasm@345 888 settings.endGroup();
matthiasm@345 889
matthiasm@345 890 // don't run analyseNow() automatically -- it's a destructive operation
matthiasm@345 891 }
matthiasm@345 892
matthiasm@345 893 void
matthiasm@423 894 MainWindow::onsetAnalysisToggled()
matthiasm@423 895 {
matthiasm@423 896 QAction *a = qobject_cast<QAction *>(sender());
matthiasm@423 897 if (!a) return;
matthiasm@423 898
matthiasm@423 899 bool set = a->isChecked();
matthiasm@423 900
matthiasm@423 901 QSettings settings;
matthiasm@423 902 settings.beginGroup("Analyser");
matthiasm@423 903 settings.setValue("onset-analysis", set);
matthiasm@423 904 settings.endGroup();
matthiasm@423 905
matthiasm@423 906 // don't run analyseNow() automatically -- it's a destructive operation
matthiasm@423 907 }
matthiasm@423 908
matthiasm@423 909 void
matthiasm@423 910 MainWindow::pruneAnalysisToggled()
matthiasm@423 911 {
matthiasm@423 912 QAction *a = qobject_cast<QAction *>(sender());
matthiasm@423 913 if (!a) return;
matthiasm@423 914
matthiasm@423 915 bool set = a->isChecked();
matthiasm@423 916
matthiasm@423 917 QSettings settings;
matthiasm@423 918 settings.beginGroup("Analyser");
matthiasm@423 919 settings.setValue("prune-analysis", set);
matthiasm@423 920 settings.endGroup();
matthiasm@423 921
matthiasm@423 922 // don't run analyseNow() automatically -- it's a destructive operation
matthiasm@423 923 }
matthiasm@423 924
matthiasm@423 925 void
Chris@0 926 MainWindow::setupHelpMenu()
Chris@0 927 {
Chris@0 928 QMenu *menu = menuBar()->addMenu(tr("&Help"));
Chris@0 929 menu->setTearOffEnabled(true);
Chris@0 930
Chris@0 931 m_keyReference->setCategory(tr("Help"));
Chris@0 932
Chris@0 933 IconLoader il;
Chris@0 934
Chris@1 935 QString name = QApplication::applicationName();
matthiasm@367 936 QAction *action;
matthiasm@367 937
Chris@585 938 action = new QAction(il.load("help"),
Chris@585 939 tr("&Help Reference"), this);
Chris@585 940 action->setShortcut(tr("F1"));
Chris@585 941 action->setStatusTip(tr("Open the %1 reference manual").arg(name));
Chris@585 942 connect(action, SIGNAL(triggered()), this, SLOT(help()));
Chris@585 943 m_keyReference->registerShortcut(action);
Chris@585 944 menu->addAction(action);
Chris@585 945
Chris@0 946 action = new QAction(tr("&Key and Mouse Reference"), this);
Chris@0 947 action->setShortcut(tr("F2"));
Chris@1 948 action->setStatusTip(tr("Open a window showing the keystrokes you can use in %1").arg(name));
Chris@0 949 connect(action, SIGNAL(triggered()), this, SLOT(keyReference()));
Chris@0 950 m_keyReference->registerShortcut(action);
Chris@0 951 menu->addAction(action);
Chris@0 952
Chris@585 953 action = new QAction(tr("What's &New In This Release?"), this);
Chris@585 954 action->setStatusTip(tr("List the changes in this release (and every previous release) of %1").arg(name));
Chris@585 955 connect(action, SIGNAL(triggered()), this, SLOT(whatsNew()));
Chris@0 956 menu->addAction(action);
Chris@0 957
Chris@1 958 action = new QAction(tr("&About %1").arg(name), this);
Chris@1 959 action->setStatusTip(tr("Show information about %1").arg(name));
Chris@0 960 connect(action, SIGNAL(triggered()), this, SLOT(about()));
Chris@0 961 menu->addAction(action);
Chris@0 962 }
Chris@0 963
Chris@0 964 void
Chris@0 965 MainWindow::setupRecentFilesMenu()
Chris@0 966 {
Chris@0 967 m_recentFilesMenu->clear();
Chris@0 968 vector<QString> files = m_recentFiles.getRecent();
Chris@0 969 for (size_t i = 0; i < files.size(); ++i) {
Chris@50 970 QAction *action = new QAction(files[i], this);
Chris@50 971 connect(action, SIGNAL(triggered()), this, SLOT(openRecentFile()));
Chris@0 972 if (i == 0) {
Chris@0 973 action->setShortcut(tr("Ctrl+R"));
Chris@0 974 m_keyReference->registerShortcut
Chris@0 975 (tr("Re-open"),
Chris@50 976 action->shortcut().toString(),
Chris@0 977 tr("Re-open the current or most recently opened file"));
Chris@0 978 }
Chris@50 979 m_recentFilesMenu->addAction(action);
Chris@0 980 }
Chris@0 981 }
Chris@0 982
Chris@0 983 void
Chris@0 984 MainWindow::setupToolbars()
Chris@0 985 {
Chris@0 986 m_keyReference->setCategory(tr("Playback and Transport Controls"));
Chris@0 987
Chris@0 988 IconLoader il;
Chris@0 989
Chris@0 990 QMenu *menu = m_playbackMenu = menuBar()->addMenu(tr("Play&back"));
Chris@0 991 menu->setTearOffEnabled(true);
Chris@0 992 m_rightButtonMenu->addSeparator();
Chris@0 993 m_rightButtonPlaybackMenu = m_rightButtonMenu->addMenu(tr("Playback"));
Chris@0 994
Chris@0 995 QToolBar *toolbar = addToolBar(tr("Playback Toolbar"));
Chris@0 996
Chris@0 997 QAction *rwdStartAction = toolbar->addAction(il.load("rewind-start"),
Chris@0 998 tr("Rewind to Start"));
Chris@0 999 rwdStartAction->setShortcut(tr("Home"));
Chris@0 1000 rwdStartAction->setStatusTip(tr("Rewind to the start"));
Chris@0 1001 connect(rwdStartAction, SIGNAL(triggered()), this, SLOT(rewindStart()));
Chris@0 1002 connect(this, SIGNAL(canPlay(bool)), rwdStartAction, SLOT(setEnabled(bool)));
Chris@0 1003
Chris@0 1004 QAction *m_rwdAction = toolbar->addAction(il.load("rewind"),
Chris@0 1005 tr("Rewind"));
Chris@300 1006 m_rwdAction->setShortcut(tr("Left"));
Chris@300 1007 m_rwdAction->setStatusTip(tr("Rewind to the previous one-second boundary"));
Chris@0 1008 connect(m_rwdAction, SIGNAL(triggered()), this, SLOT(rewind()));
Chris@0 1009 connect(this, SIGNAL(canRewind(bool)), m_rwdAction, SLOT(setEnabled(bool)));
Chris@0 1010
Chris@300 1011 setDefaultFfwdRwdStep(RealTime(1, 0));
Chris@300 1012
Chris@0 1013 QAction *playAction = toolbar->addAction(il.load("playpause"),
Chris@0 1014 tr("Play / Pause"));
Chris@0 1015 playAction->setCheckable(true);
Chris@0 1016 playAction->setShortcut(tr("Space"));
Chris@0 1017 playAction->setStatusTip(tr("Start or stop playback from the current position"));
Chris@0 1018 connect(playAction, SIGNAL(triggered()), this, SLOT(play()));
Chris@0 1019 connect(m_playSource, SIGNAL(playStatusChanged(bool)),
gyorgyf@27 1020 playAction, SLOT(setChecked(bool)));
Chris@0 1021 connect(this, SIGNAL(canPlay(bool)), playAction, SLOT(setEnabled(bool)));
Chris@0 1022
Chris@0 1023 m_ffwdAction = toolbar->addAction(il.load("ffwd"),
Chris@0 1024 tr("Fast Forward"));
Chris@300 1025 m_ffwdAction->setShortcut(tr("Right"));
Chris@300 1026 m_ffwdAction->setStatusTip(tr("Fast-forward to the next one-second boundary"));
Chris@0 1027 connect(m_ffwdAction, SIGNAL(triggered()), this, SLOT(ffwd()));
Chris@0 1028 connect(this, SIGNAL(canFfwd(bool)), m_ffwdAction, SLOT(setEnabled(bool)));
Chris@0 1029
Chris@0 1030 QAction *ffwdEndAction = toolbar->addAction(il.load("ffwd-end"),
Chris@0 1031 tr("Fast Forward to End"));
Chris@0 1032 ffwdEndAction->setShortcut(tr("End"));
Chris@0 1033 ffwdEndAction->setStatusTip(tr("Fast-forward to the end"));
Chris@0 1034 connect(ffwdEndAction, SIGNAL(triggered()), this, SLOT(ffwdEnd()));
Chris@0 1035 connect(this, SIGNAL(canPlay(bool)), ffwdEndAction, SLOT(setEnabled(bool)));
Chris@6 1036
Chris@476 1037 QAction *recordAction = toolbar->addAction(il.load("record"),
Chris@476 1038 tr("Record"));
Chris@476 1039 recordAction->setCheckable(true);
Chris@476 1040 recordAction->setShortcut(tr("Ctrl+Space"));
Chris@476 1041 recordAction->setStatusTip(tr("Record a new audio file"));
Chris@476 1042 connect(recordAction, SIGNAL(triggered()), this, SLOT(record()));
Chris@476 1043 connect(m_recordTarget, SIGNAL(recordStatusChanged(bool)),
Chris@476 1044 recordAction, SLOT(setChecked(bool)));
Chris@497 1045 connect(m_recordTarget, SIGNAL(recordCompleted()),
Chris@497 1046 this, SLOT(analyseNow()));
Chris@476 1047 connect(this, SIGNAL(canRecord(bool)),
Chris@476 1048 recordAction, SLOT(setEnabled(bool)));
Chris@476 1049
Chris@0 1050 toolbar = addToolBar(tr("Play Mode Toolbar"));
Chris@0 1051
Chris@0 1052 QAction *psAction = toolbar->addAction(il.load("playselection"),
Chris@0 1053 tr("Constrain Playback to Selection"));
Chris@0 1054 psAction->setCheckable(true);
Chris@0 1055 psAction->setChecked(m_viewManager->getPlaySelectionMode());
Chris@0 1056 psAction->setShortcut(tr("s"));
Chris@0 1057 psAction->setStatusTip(tr("Constrain playback to the selected regions"));
Chris@0 1058 connect(m_viewManager, SIGNAL(playSelectionModeChanged(bool)),
Chris@0 1059 psAction, SLOT(setChecked(bool)));
Chris@0 1060 connect(psAction, SIGNAL(triggered()), this, SLOT(playSelectionToggled()));
Chris@0 1061 connect(this, SIGNAL(canPlaySelection(bool)), psAction, SLOT(setEnabled(bool)));
Chris@0 1062
Chris@0 1063 QAction *plAction = toolbar->addAction(il.load("playloop"),
Chris@0 1064 tr("Loop Playback"));
Chris@0 1065 plAction->setCheckable(true);
Chris@0 1066 plAction->setChecked(m_viewManager->getPlayLoopMode());
Chris@0 1067 plAction->setShortcut(tr("l"));
Chris@0 1068 plAction->setStatusTip(tr("Loop playback"));
Chris@0 1069 connect(m_viewManager, SIGNAL(playLoopModeChanged(bool)),
Chris@0 1070 plAction, SLOT(setChecked(bool)));
Chris@0 1071 connect(plAction, SIGNAL(triggered()), this, SLOT(playLoopToggled()));
Chris@0 1072 connect(this, SIGNAL(canPlay(bool)), plAction, SLOT(setEnabled(bool)));
Chris@0 1073
Chris@300 1074 QAction *oneLeftAction = new QAction(tr("&One Note Left"), this);
Chris@300 1075 oneLeftAction->setShortcut(tr("Ctrl+Left"));
Chris@300 1076 oneLeftAction->setStatusTip(tr("Move cursor to the preceding note (or silence) onset."));
Chris@300 1077 connect(oneLeftAction, SIGNAL(triggered()), this, SLOT(moveOneNoteLeft()));
Chris@300 1078 connect(this, SIGNAL(canScroll(bool)), oneLeftAction, SLOT(setEnabled(bool)));
Chris@300 1079
Chris@300 1080 QAction *oneRightAction = new QAction(tr("O&ne Note Right"), this);
Chris@300 1081 oneRightAction->setShortcut(tr("Ctrl+Right"));
Chris@300 1082 oneRightAction->setStatusTip(tr("Move cursor to the succeeding note (or silence)."));
Chris@300 1083 connect(oneRightAction, SIGNAL(triggered()), this, SLOT(moveOneNoteRight()));
Chris@300 1084 connect(this, SIGNAL(canScroll(bool)), oneRightAction, SLOT(setEnabled(bool)));
Chris@300 1085
Chris@300 1086 QAction *selectOneLeftAction = new QAction(tr("&Select One Note Left"), this);
Chris@300 1087 selectOneLeftAction->setShortcut(tr("Ctrl+Shift+Left"));
Chris@300 1088 selectOneLeftAction->setStatusTip(tr("Select to the preceding note (or silence) onset."));
Chris@300 1089 connect(selectOneLeftAction, SIGNAL(triggered()), this, SLOT(selectOneNoteLeft()));
Chris@300 1090 connect(this, SIGNAL(canScroll(bool)), selectOneLeftAction, SLOT(setEnabled(bool)));
Chris@300 1091
Chris@300 1092 QAction *selectOneRightAction = new QAction(tr("S&elect One Note Right"), this);
Chris@300 1093 selectOneRightAction->setShortcut(tr("Ctrl+Shift+Right"));
Chris@300 1094 selectOneRightAction->setStatusTip(tr("Select to the succeeding note (or silence)."));
Chris@300 1095 connect(selectOneRightAction, SIGNAL(triggered()), this, SLOT(selectOneNoteRight()));
Chris@300 1096 connect(this, SIGNAL(canScroll(bool)), selectOneRightAction, SLOT(setEnabled(bool)));
Chris@300 1097
Chris@0 1098 m_keyReference->registerShortcut(psAction);
Chris@0 1099 m_keyReference->registerShortcut(plAction);
Chris@0 1100 m_keyReference->registerShortcut(playAction);
Chris@476 1101 m_keyReference->registerShortcut(recordAction);
Chris@0 1102 m_keyReference->registerShortcut(m_rwdAction);
Chris@0 1103 m_keyReference->registerShortcut(m_ffwdAction);
Chris@0 1104 m_keyReference->registerShortcut(rwdStartAction);
Chris@0 1105 m_keyReference->registerShortcut(ffwdEndAction);
Chris@476 1106 m_keyReference->registerShortcut(recordAction);
Chris@300 1107 m_keyReference->registerShortcut(oneLeftAction);
Chris@300 1108 m_keyReference->registerShortcut(oneRightAction);
Chris@300 1109 m_keyReference->registerShortcut(selectOneLeftAction);
Chris@300 1110 m_keyReference->registerShortcut(selectOneRightAction);
Chris@0 1111
Chris@6 1112 menu->addAction(playAction);
Chris@0 1113 menu->addAction(psAction);
Chris@0 1114 menu->addAction(plAction);
Chris@0 1115 menu->addSeparator();
Chris@0 1116 menu->addAction(m_rwdAction);
Chris@0 1117 menu->addAction(m_ffwdAction);
Chris@0 1118 menu->addSeparator();
Chris@0 1119 menu->addAction(rwdStartAction);
Chris@0 1120 menu->addAction(ffwdEndAction);
Chris@0 1121 menu->addSeparator();
Chris@300 1122 menu->addAction(oneLeftAction);
Chris@300 1123 menu->addAction(oneRightAction);
Chris@300 1124 menu->addAction(selectOneLeftAction);
Chris@300 1125 menu->addAction(selectOneRightAction);
Chris@300 1126 menu->addSeparator();
Chris@476 1127 menu->addAction(recordAction);
Chris@476 1128 menu->addSeparator();
Chris@0 1129
Chris@0 1130 m_rightButtonPlaybackMenu->addAction(playAction);
Chris@0 1131 m_rightButtonPlaybackMenu->addAction(psAction);
Chris@0 1132 m_rightButtonPlaybackMenu->addAction(plAction);
Chris@0 1133 m_rightButtonPlaybackMenu->addSeparator();
Chris@0 1134 m_rightButtonPlaybackMenu->addAction(m_rwdAction);
Chris@0 1135 m_rightButtonPlaybackMenu->addAction(m_ffwdAction);
Chris@0 1136 m_rightButtonPlaybackMenu->addSeparator();
Chris@0 1137 m_rightButtonPlaybackMenu->addAction(rwdStartAction);
Chris@0 1138 m_rightButtonPlaybackMenu->addAction(ffwdEndAction);
Chris@0 1139 m_rightButtonPlaybackMenu->addSeparator();
Chris@300 1140 m_rightButtonPlaybackMenu->addAction(oneLeftAction);
Chris@300 1141 m_rightButtonPlaybackMenu->addAction(oneRightAction);
Chris@300 1142 m_rightButtonPlaybackMenu->addAction(selectOneLeftAction);
Chris@300 1143 m_rightButtonPlaybackMenu->addAction(selectOneRightAction);
Chris@300 1144 m_rightButtonPlaybackMenu->addSeparator();
Chris@476 1145 m_rightButtonPlaybackMenu->addAction(recordAction);
Chris@476 1146 m_rightButtonPlaybackMenu->addSeparator();
Chris@0 1147
Chris@0 1148 QAction *fastAction = menu->addAction(tr("Speed Up"));
Chris@0 1149 fastAction->setShortcut(tr("Ctrl+PgUp"));
Chris@0 1150 fastAction->setStatusTip(tr("Time-stretch playback to speed it up without changing pitch"));
Chris@0 1151 connect(fastAction, SIGNAL(triggered()), this, SLOT(speedUpPlayback()));
Chris@0 1152 connect(this, SIGNAL(canSpeedUpPlayback(bool)), fastAction, SLOT(setEnabled(bool)));
Chris@0 1153
Chris@0 1154 QAction *slowAction = menu->addAction(tr("Slow Down"));
Chris@0 1155 slowAction->setShortcut(tr("Ctrl+PgDown"));
Chris@0 1156 slowAction->setStatusTip(tr("Time-stretch playback to slow it down without changing pitch"));
Chris@0 1157 connect(slowAction, SIGNAL(triggered()), this, SLOT(slowDownPlayback()));
Chris@0 1158 connect(this, SIGNAL(canSlowDownPlayback(bool)), slowAction, SLOT(setEnabled(bool)));
Chris@0 1159
Chris@0 1160 QAction *normalAction = menu->addAction(tr("Restore Normal Speed"));
Chris@0 1161 normalAction->setShortcut(tr("Ctrl+Home"));
Chris@0 1162 normalAction->setStatusTip(tr("Restore non-time-stretched playback"));
Chris@0 1163 connect(normalAction, SIGNAL(triggered()), this, SLOT(restoreNormalPlayback()));
Chris@0 1164 connect(this, SIGNAL(canChangePlaybackSpeed(bool)), normalAction, SLOT(setEnabled(bool)));
Chris@0 1165
Chris@0 1166 m_keyReference->registerShortcut(fastAction);
Chris@0 1167 m_keyReference->registerShortcut(slowAction);
Chris@0 1168 m_keyReference->registerShortcut(normalAction);
Chris@0 1169
Chris@0 1170 m_rightButtonPlaybackMenu->addAction(fastAction);
Chris@0 1171 m_rightButtonPlaybackMenu->addAction(slowAction);
Chris@0 1172 m_rightButtonPlaybackMenu->addAction(normalAction);
Chris@6 1173
Chris@195 1174 toolbar = new QToolBar(tr("Playback Controls"));
Chris@195 1175 addToolBar(Qt::BottomToolBarArea, toolbar);
Chris@195 1176
Chris@6 1177 toolbar->addWidget(m_playSpeed);
Chris@6 1178 toolbar->addWidget(m_fader);
Chris@0 1179
Chris@128 1180 toolbar = addToolBar(tr("Show and Play"));
matthiasm@258 1181 addToolBar(Qt::BottomToolBarArea, toolbar);
Chris@144 1182
Chris@144 1183 m_showAudio = toolbar->addAction(il.load("waveform"), tr("Show Audio"));
Chris@144 1184 m_showAudio->setCheckable(true);
Chris@144 1185 connect(m_showAudio, SIGNAL(triggered()), this, SLOT(showAudioToggled()));
Chris@144 1186 connect(this, SIGNAL(canPlay(bool)), m_showAudio, SLOT(setEnabled(bool)));
Chris@144 1187
Chris@424 1188 m_playAudio = toolbar->addAction(il.load("speaker"), tr("Play Audio"));
Chris@424 1189 m_playAudio->setCheckable(true);
Chris@424 1190 connect(m_playAudio, SIGNAL(triggered()), this, SLOT(playAudioToggled()));
Chris@424 1191 connect(this, SIGNAL(canPlayWaveform(bool)), m_playAudio, SLOT(setEnabled(bool)));
Chris@424 1192
Chris@440 1193 int lpwSize, bigLpwSize;
Chris@440 1194 #ifdef Q_OS_MAC
Chris@440 1195 lpwSize = m_viewManager->scalePixelSize(32); // Mac toolbars are fatter
Chris@442 1196 bigLpwSize = int(lpwSize * 2.2);
Chris@440 1197 #else
Chris@440 1198 lpwSize = m_viewManager->scalePixelSize(26);
Chris@442 1199 bigLpwSize = int(lpwSize * 2.8);
Chris@440 1200 #endif
Chris@440 1201
Chris@440 1202 m_audioLPW->setImageSize(lpwSize);
Chris@440 1203 m_audioLPW->setBigImageSize(bigLpwSize);
Chris@414 1204 toolbar->addWidget(m_audioLPW);
Chris@414 1205
Chris@290 1206 // Pitch (f0)
Chris@290 1207 QLabel *spacer = new QLabel; // blank
Chris@419 1208 spacer->setFixedWidth(m_viewManager->scalePixelSize(30));
Chris@290 1209 toolbar->addWidget(spacer);
Chris@145 1210
Chris@144 1211 m_showPitch = toolbar->addAction(il.load("values"), tr("Show Pitch Track"));
Chris@144 1212 m_showPitch->setCheckable(true);
Chris@144 1213 connect(m_showPitch, SIGNAL(triggered()), this, SLOT(showPitchToggled()));
Chris@144 1214 connect(this, SIGNAL(canPlay(bool)), m_showPitch, SLOT(setEnabled(bool)));
Chris@144 1215
Chris@405 1216 if (m_withSonification) {
Chris@424 1217 m_playPitch = toolbar->addAction(il.load("speaker"), tr("Play Pitch Track"));
Chris@424 1218 m_playPitch->setCheckable(true);
Chris@424 1219 connect(m_playPitch, SIGNAL(triggered()), this, SLOT(playPitchToggled()));
Chris@424 1220 connect(this, SIGNAL(canPlayPitch(bool)), m_playPitch, SLOT(setEnabled(bool)));
Chris@424 1221
Chris@440 1222 m_pitchLPW->setImageSize(lpwSize);
Chris@440 1223 m_pitchLPW->setBigImageSize(bigLpwSize);
Chris@404 1224 toolbar->addWidget(m_pitchLPW);
Chris@424 1225 } else {
Chris@424 1226 m_playPitch = 0;
matthiasm@366 1227 }
Chris@404 1228
Chris@290 1229 // Notes
Chris@290 1230 spacer = new QLabel;
Chris@419 1231 spacer->setFixedWidth(m_viewManager->scalePixelSize(30));
Chris@290 1232 toolbar->addWidget(spacer);
Chris@290 1233
Chris@144 1234 m_showNotes = toolbar->addAction(il.load("notes"), tr("Show Notes"));
Chris@144 1235 m_showNotes->setCheckable(true);
Chris@144 1236 connect(m_showNotes, SIGNAL(triggered()), this, SLOT(showNotesToggled()));
Chris@144 1237 connect(this, SIGNAL(canPlay(bool)), m_showNotes, SLOT(setEnabled(bool)));
justin@156 1238
Chris@405 1239 if (m_withSonification) {
Chris@424 1240 m_playNotes = toolbar->addAction(il.load("speaker"), tr("Play Notes"));
Chris@424 1241 m_playNotes->setCheckable(true);
Chris@424 1242 connect(m_playNotes, SIGNAL(triggered()), this, SLOT(playNotesToggled()));
Chris@424 1243 connect(this, SIGNAL(canPlayNotes(bool)), m_playNotes, SLOT(setEnabled(bool)));
Chris@424 1244
Chris@440 1245 m_notesLPW->setImageSize(lpwSize);
Chris@440 1246 m_notesLPW->setBigImageSize(bigLpwSize);
Chris@404 1247 toolbar->addWidget(m_notesLPW);
Chris@424 1248 } else {
Chris@424 1249 m_playNotes = 0;
matthiasm@366 1250 }
justin@156 1251
justin@156 1252 // Spectrogram
Chris@290 1253 spacer = new QLabel;
Chris@419 1254 spacer->setFixedWidth(m_viewManager->scalePixelSize(30));
Chris@290 1255 toolbar->addWidget(spacer);
Chris@290 1256
matthiasm@366 1257 if (!m_withSpectrogram)
matthiasm@366 1258 {
matthiasm@366 1259 m_showSpect = new QAction(tr("Show Spectrogram"), this);
matthiasm@366 1260 } else {
matthiasm@366 1261 m_showSpect = toolbar->addAction(il.load("spectrogram"), tr("Show Spectrogram"));
matthiasm@366 1262 }
justin@156 1263 m_showSpect->setCheckable(true);
justin@156 1264 connect(m_showSpect, SIGNAL(triggered()), this, SLOT(showSpectToggled()));
justin@156 1265 connect(this, SIGNAL(canPlay(bool)), m_showSpect, SLOT(setEnabled(bool)));
Chris@128 1266
Chris@0 1267 Pane::registerShortcuts(*m_keyReference);
matthiasm@366 1268
Chris@405 1269 updateLayerStatuses();
Chris@609 1270
Chris@609 1271 QTimer::singleShot(500, this, SLOT(betaReleaseWarning()));
Chris@0 1272 }
Chris@0 1273
matthiasm@281 1274
matthiasm@281 1275 void
matthiasm@281 1276 MainWindow::moveOneNoteRight()
matthiasm@281 1277 {
matthiasm@281 1278 // cerr << "MainWindow::moveOneNoteRight" << endl;
matthiasm@296 1279 moveByOneNote(true, false);
matthiasm@281 1280 }
matthiasm@281 1281
matthiasm@281 1282 void
matthiasm@281 1283 MainWindow::moveOneNoteLeft()
matthiasm@281 1284 {
matthiasm@281 1285 // cerr << "MainWindow::moveOneNoteLeft" << endl;
matthiasm@296 1286 moveByOneNote(false, false);
matthiasm@281 1287 }
matthiasm@281 1288
matthiasm@281 1289 void
matthiasm@283 1290 MainWindow::selectOneNoteRight()
matthiasm@283 1291 {
matthiasm@296 1292 moveByOneNote(true, true);
matthiasm@283 1293 }
matthiasm@283 1294
matthiasm@283 1295 void
matthiasm@283 1296 MainWindow::selectOneNoteLeft()
matthiasm@283 1297 {
matthiasm@296 1298 moveByOneNote(false, true);
matthiasm@283 1299 }
matthiasm@283 1300
matthiasm@283 1301
matthiasm@283 1302 void
matthiasm@296 1303 MainWindow::moveByOneNote(bool right, bool doSelect)
matthiasm@281 1304 {
Chris@399 1305 sv_frame_t frame = m_viewManager->getPlaybackFrame();
matthiasm@301 1306 cerr << "MainWindow::moveByOneNote startframe: " << frame << endl;
matthiasm@281 1307
matthiasm@304 1308 bool isAtSelectionBoundary = false;
matthiasm@304 1309 MultiSelection::SelectionList selections = m_viewManager->getSelections();
matthiasm@304 1310 if (!selections.empty()) {
matthiasm@304 1311 Selection sel = *selections.begin();
matthiasm@342 1312 isAtSelectionBoundary = (frame == sel.getStartFrame()) || (frame == sel.getEndFrame());
matthiasm@304 1313 }
matthiasm@304 1314 if (!doSelect || !isAtSelectionBoundary) {
matthiasm@296 1315 m_selectionAnchor = frame;
matthiasm@296 1316 }
matthiasm@296 1317
matthiasm@281 1318 Layer *layer = m_analyser->getLayer(Analyser::Notes);
matthiasm@281 1319 if (!layer) return;
matthiasm@281 1320
Chris@573 1321 auto model = ModelById::getAs<NoteModel>(layer->getModel());
matthiasm@281 1322 if (!model) return;
matthiasm@281 1323
Chris@570 1324 //!!! This seems like a strange and inefficient way to do this -
Chris@570 1325 //!!! there is almost certainly a better way making use of
Chris@570 1326 //!!! EventSeries api
Chris@570 1327
Chris@570 1328 EventVector points = model->getAllEvents();
matthiasm@281 1329 if (points.empty()) return;
matthiasm@281 1330
Chris@570 1331 EventVector::iterator i = points.begin();
Chris@399 1332 std::set<sv_frame_t> snapFrames;
matthiasm@281 1333 snapFrames.insert(0);
matthiasm@281 1334 while (i != points.end()) {
Chris@570 1335 snapFrames.insert(i->getFrame());
Chris@570 1336 snapFrames.insert(i->getFrame() + i->getDuration() + 1);
matthiasm@281 1337 ++i;
matthiasm@281 1338 }
Chris@399 1339 std::set<sv_frame_t>::iterator i2;
Chris@399 1340 if (snapFrames.find(frame) == snapFrames.end()) {
matthiasm@281 1341 // we're not on an existing snap point, so go to previous
matthiasm@281 1342 snapFrames.insert(frame);
matthiasm@281 1343 }
matthiasm@281 1344 i2 = snapFrames.find(frame);
Chris@399 1345 if (right) {
matthiasm@281 1346 i2++;
matthiasm@281 1347 if (i2 == snapFrames.end()) i2--;
matthiasm@281 1348 } else {
matthiasm@281 1349 if (i2 != snapFrames.begin()) i2--;
matthiasm@281 1350 }
matthiasm@281 1351 frame = *i2;
matthiasm@281 1352 m_viewManager->setPlaybackFrame(frame);
matthiasm@296 1353 if (doSelect) {
matthiasm@296 1354 Selection sel;
matthiasm@296 1355 if (frame > m_selectionAnchor) {
matthiasm@342 1356 sel = Selection(m_selectionAnchor, frame);
matthiasm@296 1357 } else {
matthiasm@342 1358 sel = Selection(frame, m_selectionAnchor);
matthiasm@296 1359 }
matthiasm@296 1360 m_viewManager->setSelection(sel);
matthiasm@296 1361 }
matthiasm@301 1362 cerr << "MainWindow::moveByOneNote endframe: " << frame << endl;
matthiasm@281 1363 }
matthiasm@281 1364
Chris@0 1365 void
Chris@70 1366 MainWindow::toolNavigateSelected()
Chris@70 1367 {
Chris@70 1368 m_viewManager->setToolMode(ViewManager::NavigateMode);
Chris@70 1369 m_intelligentActionOn = true;
Chris@70 1370 }
Chris@70 1371
Chris@70 1372 void
Chris@70 1373 MainWindow::toolEditSelected()
Chris@70 1374 {
matthiasm@294 1375 cerr << "MainWindow::toolEditSelected" << endl;
Chris@77 1376 m_viewManager->setToolMode(ViewManager::NoteEditMode);
Chris@70 1377 m_intelligentActionOn = true;
Chris@70 1378 m_analyser->setIntelligentActions(m_intelligentActionOn);
Chris@70 1379 }
Chris@70 1380
Chris@70 1381 void
Chris@70 1382 MainWindow::toolFreeEditSelected()
Chris@70 1383 {
Chris@77 1384 m_viewManager->setToolMode(ViewManager::NoteEditMode);
Chris@70 1385 m_intelligentActionOn = false;
Chris@70 1386 m_analyser->setIntelligentActions(m_intelligentActionOn);
Chris@70 1387 }
Chris@70 1388
gyorgyf@21 1389 void
Chris@0 1390 MainWindow::updateMenuStates()
Chris@0 1391 {
Chris@0 1392 MainWindowBase::updateMenuStates();
Chris@0 1393
Chris@0 1394 Pane *currentPane = 0;
Chris@0 1395 Layer *currentLayer = 0;
Chris@0 1396
Chris@0 1397 if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
Chris@0 1398 if (currentPane) currentLayer = currentPane->getSelectedLayer();
Chris@0 1399
Chris@291 1400 bool haveMainModel =
Chris@291 1401 (getMainModel() != 0);
Chris@291 1402 bool havePlayTarget =
Chris@479 1403 (m_playTarget != 0 || m_audioIO != 0);
Chris@0 1404 bool haveCurrentPane =
Chris@0 1405 (currentPane != 0);
Chris@0 1406 bool haveCurrentLayer =
Chris@0 1407 (haveCurrentPane &&
Chris@0 1408 (currentLayer != 0));
Chris@0 1409 bool haveSelection =
Chris@187 1410 (m_viewManager &&
Chris@187 1411 !m_viewManager->getSelections().empty());
Chris@0 1412 bool haveCurrentTimeInstantsLayer =
Chris@187 1413 (haveCurrentLayer &&
Chris@187 1414 qobject_cast<TimeInstantLayer *>(currentLayer));
Chris@0 1415 bool haveCurrentTimeValueLayer =
Chris@187 1416 (haveCurrentLayer &&
Chris@187 1417 qobject_cast<TimeValueLayer *>(currentLayer));
Chris@187 1418 bool pitchCandidatesVisible =
Chris@187 1419 m_analyser->arePitchCandidatesShown();
Chris@0 1420
Chris@0 1421 emit canChangePlaybackSpeed(true);
Chris@0 1422 int v = m_playSpeed->value();
Chris@0 1423 emit canSpeedUpPlayback(v < m_playSpeed->maximum());
Chris@0 1424 emit canSlowDownPlayback(v > m_playSpeed->minimum());
Chris@0 1425
Chris@291 1426 bool haveWaveform =
Chris@291 1427 m_analyser->isVisible(Analyser::Audio) &&
Chris@291 1428 m_analyser->getLayer(Analyser::Audio);
Chris@291 1429
Chris@237 1430 bool havePitchTrack =
Chris@237 1431 m_analyser->isVisible(Analyser::PitchTrack) &&
Chris@237 1432 m_analyser->getLayer(Analyser::PitchTrack);
Chris@237 1433
Chris@237 1434 bool haveNotes =
Chris@237 1435 m_analyser->isVisible(Analyser::Notes) &&
Chris@237 1436 m_analyser->getLayer(Analyser::Notes);
Chris@237 1437
Chris@237 1438 emit canExportPitchTrack(havePitchTrack);
Chris@237 1439 emit canExportNotes(haveNotes);
Chris@237 1440 emit canSnapNotes(haveSelection && haveNotes);
Chris@224 1441
Chris@291 1442 emit canPlayWaveform(haveWaveform && haveMainModel && havePlayTarget);
Chris@291 1443 emit canPlayPitch(havePitchTrack && haveMainModel && havePlayTarget);
Chris@291 1444 emit canPlayNotes(haveNotes && haveMainModel && havePlayTarget);
Chris@291 1445
Chris@187 1446 if (pitchCandidatesVisible) {
Chris@187 1447 m_showCandidatesAction->setText(tr("Hide Pitch Candidates"));
Chris@187 1448 m_showCandidatesAction->setStatusTip(tr("Remove the display of alternate pitch candidates for the selected region"));
Chris@187 1449 } else {
Chris@187 1450 m_showCandidatesAction->setText(tr("Show Pitch Candidates"));
Chris@187 1451 m_showCandidatesAction->setStatusTip(tr("Show alternate pitch candidates for the selected region"));
Chris@187 1452 }
Chris@187 1453
Chris@0 1454 if (m_ffwdAction && m_rwdAction) {
Chris@0 1455 if (haveCurrentTimeInstantsLayer) {
Chris@0 1456 m_ffwdAction->setText(tr("Fast Forward to Next Instant"));
Chris@0 1457 m_ffwdAction->setStatusTip(tr("Fast forward to the next time instant in the current layer"));
Chris@0 1458 m_rwdAction->setText(tr("Rewind to Previous Instant"));
Chris@0 1459 m_rwdAction->setStatusTip(tr("Rewind to the previous time instant in the current layer"));
Chris@0 1460 } else if (haveCurrentTimeValueLayer) {
Chris@0 1461 m_ffwdAction->setText(tr("Fast Forward to Next Point"));
Chris@0 1462 m_ffwdAction->setStatusTip(tr("Fast forward to the next point in the current layer"));
Chris@0 1463 m_rwdAction->setText(tr("Rewind to Previous Point"));
Chris@0 1464 m_rwdAction->setStatusTip(tr("Rewind to the previous point in the current layer"));
Chris@0 1465 } else {
Chris@0 1466 m_ffwdAction->setText(tr("Fast Forward"));
Chris@0 1467 m_ffwdAction->setStatusTip(tr("Fast forward"));
Chris@0 1468 m_rwdAction->setText(tr("Rewind"));
Chris@0 1469 m_rwdAction->setStatusTip(tr("Rewind"));
Chris@0 1470 }
Chris@0 1471 }
Chris@0 1472 }
Chris@0 1473
Chris@0 1474 void
Chris@144 1475 MainWindow::showAudioToggled()
Chris@128 1476 {
Chris@144 1477 m_analyser->toggleVisible(Analyser::Audio);
justin@220 1478
Chris@424 1479 QSettings settings;
Chris@424 1480 settings.beginGroup("MainWindow");
Chris@424 1481
Chris@424 1482 bool playOn = false;
Chris@424 1483 if (m_analyser->isVisible(Analyser::Audio)) {
Chris@424 1484 // just switched layer on; check whether playback was also on previously
Chris@424 1485 playOn = settings.value("playaudiowas", true).toBool();
Chris@424 1486 } else {
Chris@424 1487 settings.setValue("playaudiowas", m_playAudio->isChecked());
Chris@424 1488 }
Chris@291 1489 m_analyser->setAudible(Analyser::Audio, playOn);
Chris@424 1490
Chris@424 1491 settings.endGroup();
Chris@291 1492
Chris@291 1493 updateMenuStates();
Chris@424 1494 updateLayerStatuses();
Chris@128 1495 }
Chris@128 1496
Chris@128 1497 void
Chris@144 1498 MainWindow::showPitchToggled()
Chris@128 1499 {
Chris@144 1500 m_analyser->toggleVisible(Analyser::PitchTrack);
justin@185 1501
Chris@424 1502 QSettings settings;
Chris@424 1503 settings.beginGroup("MainWindow");
Chris@424 1504
Chris@424 1505 bool playOn = false;
Chris@424 1506 if (m_analyser->isVisible(Analyser::PitchTrack)) {
Chris@424 1507 // just switched layer on; check whether playback was also on previously
Chris@424 1508 playOn = settings.value("playpitchwas", true).toBool();
Chris@424 1509 } else {
Chris@424 1510 settings.setValue("playpitchwas", m_playPitch->isChecked());
Chris@424 1511 }
Chris@424 1512 m_analyser->setAudible(Analyser::PitchTrack, playOn);
Chris@424 1513
Chris@424 1514 settings.endGroup();
Chris@291 1515
Chris@291 1516 updateMenuStates();
Chris@424 1517 updateLayerStatuses();
Chris@128 1518 }
Chris@128 1519
Chris@128 1520 void
Chris@145 1521 MainWindow::showSpectToggled()
Chris@145 1522 {
Chris@145 1523 m_analyser->toggleVisible(Analyser::Spectrogram);
Chris@145 1524 }
Chris@145 1525
Chris@145 1526 void
Chris@144 1527 MainWindow::showNotesToggled()
Chris@128 1528 {
Chris@144 1529 m_analyser->toggleVisible(Analyser::Notes);
justin@220 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::Notes)) {
Chris@424 1536 // just switched layer on; check whether playback was also on previously
Chris@424 1537 playOn = settings.value("playnoteswas", true).toBool();
Chris@424 1538 } else {
Chris@424 1539 settings.setValue("playnoteswas", m_playNotes->isChecked());
Chris@424 1540 }
Chris@424 1541 m_analyser->setAudible(Analyser::Notes, playOn);
Chris@424 1542
Chris@424 1543 settings.endGroup();
Chris@291 1544
Chris@291 1545 updateMenuStates();
Chris@424 1546 updateLayerStatuses();
Chris@144 1547 }
Chris@144 1548
Chris@144 1549 void
Chris@144 1550 MainWindow::playAudioToggled()
Chris@144 1551 {
Chris@144 1552 m_analyser->toggleAudible(Analyser::Audio);
Chris@424 1553 updateLayerStatuses();
Chris@144 1554 }
Chris@144 1555
Chris@144 1556 void
Chris@144 1557 MainWindow::playPitchToggled()
Chris@144 1558 {
Chris@144 1559 m_analyser->toggleAudible(Analyser::PitchTrack);
Chris@424 1560 updateLayerStatuses();
Chris@144 1561 }
Chris@144 1562
Chris@144 1563 void
Chris@144 1564 MainWindow::playNotesToggled()
Chris@144 1565 {
Chris@144 1566 m_analyser->toggleAudible(Analyser::Notes);
Chris@424 1567 updateLayerStatuses();
Chris@144 1568 }
Chris@144 1569
Chris@144 1570 void
Chris@144 1571 MainWindow::updateLayerStatuses()
Chris@144 1572 {
Chris@144 1573 m_showAudio->setChecked(m_analyser->isVisible(Analyser::Audio));
Chris@424 1574 m_playAudio->setChecked(m_analyser->isAudible(Analyser::Audio));
Chris@424 1575 m_audioLPW->setEnabled(m_analyser->isAudible(Analyser::Audio));
Chris@405 1576 m_audioLPW->setLevel(m_analyser->getGain(Analyser::Audio));
Chris@405 1577 m_audioLPW->setPan(m_analyser->getPan(Analyser::Audio));
Chris@424 1578
Chris@405 1579 m_showPitch->setChecked(m_analyser->isVisible(Analyser::PitchTrack));
Chris@424 1580 m_playPitch->setChecked(m_analyser->isAudible(Analyser::PitchTrack));
Chris@424 1581 m_pitchLPW->setEnabled(m_analyser->isAudible(Analyser::PitchTrack));
Chris@405 1582 m_pitchLPW->setLevel(m_analyser->getGain(Analyser::PitchTrack));
Chris@405 1583 m_pitchLPW->setPan(m_analyser->getPan(Analyser::PitchTrack));
Chris@405 1584
Chris@405 1585 m_showNotes->setChecked(m_analyser->isVisible(Analyser::Notes));
Chris@424 1586 m_playNotes->setChecked(m_analyser->isAudible(Analyser::Notes));
Chris@424 1587 m_notesLPW->setEnabled(m_analyser->isAudible(Analyser::Notes));
Chris@405 1588 m_notesLPW->setLevel(m_analyser->getGain(Analyser::Notes));
Chris@405 1589 m_notesLPW->setPan(m_analyser->getPan(Analyser::Notes));
Chris@405 1590
Chris@145 1591 m_showSpect->setChecked(m_analyser->isVisible(Analyser::Spectrogram));
Chris@128 1592 }
Chris@128 1593
Chris@128 1594 void
Chris@227 1595 MainWindow::editDisplayExtents()
Chris@227 1596 {
Chris@399 1597 double min, max;
Chris@399 1598 double vmin = 0;
Chris@399 1599 double vmax = getMainModel()->getSampleRate() /2;
Chris@227 1600
Chris@227 1601 if (!m_analyser->getDisplayFrequencyExtents(min, max)) {
Chris@227 1602 //!!!
Chris@227 1603 return;
Chris@227 1604 }
Chris@227 1605
Chris@227 1606 RangeInputDialog dialog(tr("Set frequency range"),
Chris@227 1607 tr("Enter new frequency range, from %1 to %2 Hz.\nThese values will be rounded to the nearest spectrogram bin.")
Chris@227 1608 .arg(vmin).arg(vmax),
Chris@399 1609 "Hz", float(vmin), float(vmax), this);
Chris@399 1610 dialog.setRange(float(min), float(max));
Chris@227 1611
Chris@227 1612 if (dialog.exec() == QDialog::Accepted) {
Chris@399 1613 float fmin, fmax;
Chris@399 1614 dialog.getRange(fmin, fmax);
Chris@399 1615 min = fmin;
Chris@399 1616 max = fmax;
Chris@227 1617 if (min > max) {
Chris@399 1618 double tmp = max;
Chris@227 1619 max = min;
Chris@227 1620 min = tmp;
Chris@227 1621 }
Chris@227 1622 m_analyser->setDisplayFrequencyExtents(min, max);
Chris@227 1623 }
Chris@227 1624 }
Chris@227 1625
Chris@227 1626 void
Chris@0 1627 MainWindow::updateDescriptionLabel()
Chris@0 1628 {
Chris@6 1629 // Nothing, we don't have one
Chris@0 1630 }
Chris@0 1631
Chris@0 1632 void
Chris@0 1633 MainWindow::documentModified()
Chris@0 1634 {
Chris@0 1635 MainWindowBase::documentModified();
Chris@0 1636 }
Chris@0 1637
Chris@0 1638 void
Chris@0 1639 MainWindow::documentRestored()
Chris@0 1640 {
Chris@0 1641 MainWindowBase::documentRestored();
Chris@0 1642 }
Chris@0 1643
Chris@0 1644 void
Chris@0 1645 MainWindow::newSession()
Chris@0 1646 {
Chris@0 1647 if (!checkSaveModified()) return;
Chris@0 1648
Chris@0 1649 closeSession();
Chris@0 1650 createDocument();
Chris@0 1651 m_document->setAutoAlignment(true);
Chris@0 1652
Chris@604 1653 Pane *pane = m_paneStack->addPane();
Chris@362 1654 pane->setPlaybackFollow(PlaybackScrollPage);
Chris@0 1655
Chris@391 1656 m_viewManager->setGlobalCentreFrame
Chris@391 1657 (pane->getFrameForX(width() / 2));
Chris@391 1658
Chris@0 1659 connect(pane, SIGNAL(contextHelpChanged(const QString &)),
Chris@0 1660 this, SLOT(contextHelpChanged(const QString &)));
Chris@0 1661
Chris@6 1662 // Layer *waveform = m_document->createMainModelLayer(LayerFactory::Waveform);
Chris@6 1663 // m_document->addLayerToView(pane, waveform);
Chris@0 1664
Chris@0 1665 m_overview->registerView(pane);
Chris@0 1666
Chris@0 1667 CommandHistory::getInstance()->clear();
Chris@0 1668 CommandHistory::getInstance()->documentSaved();
Chris@0 1669 documentRestored();
Chris@0 1670 updateMenuStates();
Chris@0 1671 }
Chris@0 1672
Chris@0 1673 void
Chris@244 1674 MainWindow::documentReplaced()
Chris@244 1675 {
Chris@244 1676 if (m_document) {
Chris@244 1677 connect(m_document, SIGNAL(activity(QString)),
Chris@244 1678 m_activityLog, SLOT(activityHappened(QString)));
Chris@244 1679 }
Chris@244 1680 }
Chris@244 1681
Chris@244 1682 void
Chris@0 1683 MainWindow::closeSession()
Chris@0 1684 {
Chris@0 1685 if (!checkSaveModified()) return;
Chris@0 1686
Chris@226 1687 m_analyser->fileClosed();
Chris@226 1688
Chris@0 1689 while (m_paneStack->getPaneCount() > 0) {
Chris@0 1690
Chris@167 1691 Pane *pane = m_paneStack->getPane(m_paneStack->getPaneCount() - 1);
Chris@0 1692
Chris@167 1693 while (pane->getLayerCount() > 0) {
Chris@167 1694 m_document->removeLayerFromView
Chris@167 1695 (pane, pane->getLayer(pane->getLayerCount() - 1));
Chris@167 1696 }
Chris@167 1697
Chris@167 1698 m_overview->unregisterView(pane);
Chris@167 1699 m_paneStack->deletePane(pane);
Chris@0 1700 }
Chris@0 1701
Chris@0 1702 while (m_paneStack->getHiddenPaneCount() > 0) {
Chris@0 1703
Chris@167 1704 Pane *pane = m_paneStack->getHiddenPane
Chris@167 1705 (m_paneStack->getHiddenPaneCount() - 1);
Chris@167 1706
Chris@167 1707 while (pane->getLayerCount() > 0) {
Chris@167 1708 m_document->removeLayerFromView
Chris@167 1709 (pane, pane->getLayer(pane->getLayerCount() - 1));
Chris@167 1710 }
Chris@167 1711
Chris@167 1712 m_overview->unregisterView(pane);
Chris@167 1713 m_paneStack->deletePane(pane);
Chris@0 1714 }
Chris@0 1715
Chris@0 1716 delete m_document;
Chris@0 1717 m_document = 0;
Chris@0 1718 m_viewManager->clearSelections();
Chris@0 1719 m_timeRulerLayer = 0; // document owned this
Chris@0 1720
Chris@0 1721 m_sessionFile = "";
Chris@0 1722
Chris@0 1723 CommandHistory::getInstance()->clear();
Chris@0 1724 CommandHistory::getInstance()->documentSaved();
Chris@0 1725 documentRestored();
Chris@0 1726 }
Chris@0 1727
Chris@0 1728 void
Chris@0 1729 MainWindow::openFile()
Chris@0 1730 {
Chris@0 1731 QString orig = m_audioFile;
Chris@0 1732 if (orig == "") orig = ".";
Chris@0 1733 else orig = QFileInfo(orig).absoluteDir().canonicalPath();
Chris@0 1734
Chris@0 1735 QString path = getOpenFileName(FileFinder::AnyFile);
Chris@0 1736
Chris@0 1737 if (path.isEmpty()) return;
Chris@0 1738
Chris@387 1739 FileOpenStatus status = openPath(path, ReplaceSession);
Chris@0 1740
Chris@0 1741 if (status == FileOpenFailed) {
Chris@0 1742 QMessageBox::critical(this, tr("Failed to open file"),
Chris@0 1743 tr("<b>File open failed</b><p>File \"%1\" could not be opened").arg(path));
Chris@0 1744 } else if (status == FileOpenWrongMode) {
Chris@0 1745 QMessageBox::critical(this, tr("Failed to open file"),
Chris@0 1746 tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
Chris@0 1747 }
Chris@0 1748 }
Chris@0 1749
Chris@0 1750 void
Chris@0 1751 MainWindow::openLocation()
Chris@0 1752 {
Chris@0 1753 QSettings settings;
Chris@0 1754 settings.beginGroup("MainWindow");
Chris@0 1755 QString lastLocation = settings.value("lastremote", "").toString();
Chris@0 1756
Chris@0 1757 bool ok = false;
Chris@0 1758 QString text = QInputDialog::getText
Chris@0 1759 (this, tr("Open Location"),
Chris@0 1760 tr("Please enter the URL of the location to open:"),
Chris@0 1761 QLineEdit::Normal, lastLocation, &ok);
Chris@0 1762
Chris@0 1763 if (!ok) return;
Chris@0 1764
Chris@0 1765 settings.setValue("lastremote", text);
Chris@0 1766
Chris@0 1767 if (text.isEmpty()) return;
Chris@0 1768
Chris@387 1769 FileOpenStatus status = openPath(text, ReplaceSession);
Chris@0 1770
Chris@0 1771 if (status == FileOpenFailed) {
Chris@0 1772 QMessageBox::critical(this, tr("Failed to open location"),
Chris@0 1773 tr("<b>Open failed</b><p>URL \"%1\" could not be opened").arg(text));
Chris@0 1774 } else if (status == FileOpenWrongMode) {
Chris@0 1775 QMessageBox::critical(this, tr("Failed to open location"),
Chris@0 1776 tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
Chris@0 1777 }
Chris@0 1778 }
Chris@0 1779
Chris@0 1780 void
Chris@0 1781 MainWindow::openRecentFile()
Chris@0 1782 {
Chris@0 1783 QObject *obj = sender();
Chris@3 1784 QAction *action = qobject_cast<QAction *>(obj);
Chris@0 1785
Chris@0 1786 if (!action) {
Chris@188 1787 cerr << "WARNING: MainWindow::openRecentFile: sender is not an action"
Chris@188 1788 << endl;
Chris@188 1789 return;
Chris@0 1790 }
Chris@0 1791
Chris@0 1792 QString path = action->text();
Chris@0 1793 if (path == "") return;
Chris@0 1794
Chris@387 1795 FileOpenStatus status = openPath(path, ReplaceSession);
Chris@0 1796
Chris@0 1797 if (status == FileOpenFailed) {
Chris@0 1798 QMessageBox::critical(this, tr("Failed to open location"),
Chris@0 1799 tr("<b>Open failed</b><p>File or URL \"%1\" could not be opened").arg(path));
Chris@0 1800 } else if (status == FileOpenWrongMode) {
Chris@0 1801 QMessageBox::critical(this, tr("Failed to open location"),
Chris@0 1802 tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
Chris@0 1803 }
Chris@0 1804 }
Chris@0 1805
Chris@0 1806 void
Chris@0 1807 MainWindow::paneAdded(Pane *pane)
Chris@0 1808 {
Chris@6 1809 pane->setPlaybackFollow(PlaybackScrollPage);
Chris@0 1810 m_paneStack->sizePanesEqually();
Chris@0 1811 if (m_overview) m_overview->registerView(pane);
Chris@0 1812 }
Chris@0 1813
Chris@0 1814 void
Chris@0 1815 MainWindow::paneHidden(Pane *pane)
Chris@0 1816 {
Chris@0 1817 if (m_overview) m_overview->unregisterView(pane);
Chris@0 1818 }
Chris@0 1819
Chris@0 1820 void
Chris@0 1821 MainWindow::paneAboutToBeDeleted(Pane *pane)
Chris@0 1822 {
Chris@0 1823 if (m_overview) m_overview->unregisterView(pane);
Chris@0 1824 }
Chris@0 1825
Chris@0 1826 void
Chris@0 1827 MainWindow::paneDropAccepted(Pane *pane, QStringList uriList)
Chris@0 1828 {
Chris@4 1829 if (pane) m_paneStack->setCurrentPane(pane);
Chris@0 1830
Chris@0 1831 for (QStringList::iterator i = uriList.begin(); i != uriList.end(); ++i) {
Chris@0 1832
Chris@387 1833 FileOpenStatus status = openPath(*i, ReplaceSession);
Chris@0 1834
Chris@0 1835 if (status == FileOpenFailed) {
Chris@0 1836 QMessageBox::critical(this, tr("Failed to open dropped URL"),
Chris@0 1837 tr("<b>Open failed</b><p>Dropped URL \"%1\" could not be opened").arg(*i));
Chris@0 1838 } else if (status == FileOpenWrongMode) {
Chris@0 1839 QMessageBox::critical(this, tr("Failed to open dropped URL"),
Chris@0 1840 tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
Chris@0 1841 }
Chris@0 1842 }
Chris@0 1843 }
Chris@0 1844
Chris@0 1845 void
Chris@0 1846 MainWindow::paneDropAccepted(Pane *pane, QString text)
Chris@0 1847 {
Chris@0 1848 if (pane) m_paneStack->setCurrentPane(pane);
Chris@0 1849
Chris@0 1850 QUrl testUrl(text);
Chris@0 1851 if (testUrl.scheme() == "file" ||
Chris@0 1852 testUrl.scheme() == "http" ||
Chris@0 1853 testUrl.scheme() == "ftp") {
Chris@0 1854 QStringList list;
Chris@0 1855 list.push_back(text);
Chris@0 1856 paneDropAccepted(pane, list);
Chris@0 1857 return;
Chris@0 1858 }
Chris@0 1859
Chris@0 1860 //!!! open as text -- but by importing as if a CSV, or just adding
Chris@0 1861 //to a text layer?
Chris@0 1862 }
Chris@0 1863
Chris@0 1864 void
Chris@0 1865 MainWindow::closeEvent(QCloseEvent *e)
Chris@0 1866 {
Chris@70 1867 // cerr << "MainWindow::closeEvent" << endl;
Chris@0 1868
Chris@0 1869 if (m_openingAudioFile) {
Chris@70 1870 // cerr << "Busy - ignoring close event" << endl;
Chris@257 1871 e->ignore();
Chris@257 1872 return;
Chris@0 1873 }
Chris@0 1874
Chris@0 1875 if (!m_abandoning && !checkSaveModified()) {
Chris@70 1876 // cerr << "Ignoring close event" << endl;
Chris@257 1877 e->ignore();
Chris@257 1878 return;
Chris@0 1879 }
Chris@0 1880
Chris@0 1881 QSettings settings;
Chris@0 1882 settings.beginGroup("MainWindow");
Chris@0 1883 settings.setValue("size", size());
Chris@0 1884 settings.setValue("position", pos());
Chris@0 1885 settings.endGroup();
Chris@0 1886
Chris@0 1887 delete m_keyReference;
Chris@0 1888 m_keyReference = 0;
Chris@0 1889
Chris@0 1890 closeSession();
Chris@0 1891
Chris@0 1892 e->accept();
Chris@0 1893 return;
Chris@0 1894 }
Chris@0 1895
Chris@0 1896 bool
Chris@0 1897 MainWindow::commitData(bool mayAskUser)
Chris@0 1898 {
Chris@0 1899 if (mayAskUser) {
Chris@0 1900 bool rv = checkSaveModified();
Chris@0 1901 return rv;
Chris@0 1902 } else {
Chris@0 1903 if (!m_documentModified) return true;
Chris@0 1904
Chris@0 1905 // If we can't check with the user first, then we can't save
Chris@0 1906 // to the original session file (even if we have it) -- have
Chris@0 1907 // to use a temporary file
Chris@0 1908
Chris@0 1909 QString svDirBase = ".sv1";
Chris@0 1910 QString svDir = QDir::home().filePath(svDirBase);
Chris@0 1911
Chris@0 1912 if (!QFileInfo(svDir).exists()) {
Chris@0 1913 if (!QDir::home().mkdir(svDirBase)) return false;
Chris@0 1914 } else {
Chris@0 1915 if (!QFileInfo(svDir).isDir()) return false;
Chris@0 1916 }
Chris@0 1917
Chris@0 1918 // This name doesn't have to be unguessable
Chris@0 1919 #ifndef _WIN32
Chris@0 1920 QString fname = QString("tmp-%1-%2.sv")
Chris@0 1921 .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz"))
Chris@0 1922 .arg(QProcess().pid());
Chris@0 1923 #else
Chris@0 1924 QString fname = QString("tmp-%1.sv")
Chris@0 1925 .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz"));
Chris@0 1926 #endif
Chris@0 1927 QString fpath = QDir(svDir).filePath(fname);
Chris@0 1928 if (saveSessionFile(fpath)) {
Chris@0 1929 m_recentFiles.addFile(fpath);
Chris@0 1930 return true;
Chris@0 1931 } else {
Chris@0 1932 return false;
Chris@0 1933 }
Chris@0 1934 }
Chris@0 1935 }
Chris@0 1936
Chris@0 1937 bool
Chris@0 1938 MainWindow::checkSaveModified()
Chris@0 1939 {
Chris@0 1940 // Called before some destructive operation (e.g. new session,
Chris@0 1941 // exit program). Return true if we can safely proceed, false to
Chris@0 1942 // cancel.
Chris@0 1943
Chris@0 1944 if (!m_documentModified) return true;
Chris@0 1945
Chris@0 1946 int button =
Chris@257 1947 QMessageBox::warning(this,
Chris@257 1948 tr("Session modified"),
Chris@257 1949 tr("The current session has been modified.\nDo you want to save it?"),
Chris@257 1950 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@0 1951 QMessageBox::Yes);
Chris@0 1952
Chris@0 1953 if (button == QMessageBox::Yes) {
Chris@257 1954 saveSession();
Chris@257 1955 if (m_documentModified) { // save failed -- don't proceed!
Chris@257 1956 return false;
Chris@257 1957 } else {
Chris@0 1958 return true; // saved, so it's safe to continue now
Chris@0 1959 }
Chris@0 1960 } else if (button == QMessageBox::No) {
Chris@257 1961 m_documentModified = false; // so we know to abandon it
Chris@257 1962 return true;
Chris@0 1963 }
Chris@0 1964
Chris@0 1965 // else cancel
Chris@0 1966 return false;
Chris@0 1967 }
Chris@0 1968
Chris@314 1969 bool
Chris@314 1970 MainWindow::waitForInitialAnalysis()
Chris@314 1971 {
Chris@314 1972 // Called before saving a session. We can't safely save while the
Chris@314 1973 // initial analysis is happening, because then we end up with an
Chris@314 1974 // incomplete session on reload. There are certainly theoretically
Chris@314 1975 // better ways to handle this...
Chris@314 1976
Chris@330 1977 QSettings settings;
Chris@330 1978 settings.beginGroup("Analyser");
Chris@330 1979 bool autoAnalyse = settings.value("auto-analysis", true).toBool();
Chris@330 1980 settings.endGroup();
Chris@330 1981
Chris@330 1982 if (!autoAnalyse) {
Chris@330 1983 return true;
Chris@330 1984 }
Chris@330 1985
Chris@314 1986 if (!m_analyser || m_analyser->getInitialAnalysisCompletion() >= 100) {
Chris@314 1987 return true;
Chris@314 1988 }
Chris@314 1989
Chris@314 1990 QMessageBox mb(QMessageBox::Information,
Chris@314 1991 tr("Waiting for analysis"),
Chris@442 1992 tr("Waiting for initial analysis to finish before loading or saving..."),
Chris@314 1993 QMessageBox::Cancel,
Chris@314 1994 this);
Chris@314 1995
Chris@314 1996 connect(m_analyser, SIGNAL(initialAnalysisCompleted()),
Chris@314 1997 &mb, SLOT(accept()));
Chris@314 1998
Chris@314 1999 if (mb.exec() == QDialog::Accepted) {
Chris@314 2000 return true;
Chris@314 2001 } else {
Chris@314 2002 return false;
Chris@314 2003 }
Chris@314 2004 }
Chris@314 2005
Chris@0 2006 void
Chris@0 2007 MainWindow::saveSession()
Chris@0 2008 {
Chris@269 2009 // We do not want to save mid-analysis regions -- that would cause
Chris@269 2010 // confusion on reloading
Chris@269 2011 m_analyser->clearReAnalysis();
Chris@269 2012 clearSelection();
Chris@269 2013
Chris@0 2014 if (m_sessionFile != "") {
Chris@257 2015 if (!saveSessionFile(m_sessionFile)) {
Chris@257 2016 QMessageBox::critical
Chris@257 2017 (this, tr("Failed to save file"),
Chris@257 2018 tr("Session file \"%1\" could not be saved.").arg(m_sessionFile));
Chris@257 2019 } else {
Chris@257 2020 CommandHistory::getInstance()->documentSaved();
Chris@257 2021 documentRestored();
Chris@257 2022 }
Chris@0 2023 } else {
Chris@257 2024 saveSessionAs();
Chris@0 2025 }
Chris@0 2026 }
Chris@0 2027
Chris@0 2028 void
matthiasm@310 2029 MainWindow::saveSessionInAudioPath()
matthiasm@310 2030 {
matthiasm@311 2031 if (m_audioFile == "") return;
matthiasm@311 2032
Chris@314 2033 if (!waitForInitialAnalysis()) return;
Chris@314 2034
matthiasm@310 2035 // We do not want to save mid-analysis regions -- that would cause
matthiasm@310 2036 // confusion on reloading
matthiasm@310 2037 m_analyser->clearReAnalysis();
matthiasm@310 2038 clearSelection();
matthiasm@310 2039
matthiasm@310 2040 QString filepath = QFileInfo(m_audioFile).absoluteDir().canonicalPath();
matthiasm@310 2041 QString basename = QFileInfo(m_audioFile).completeBaseName();
matthiasm@310 2042
matthiasm@310 2043 QString path = QDir(filepath).filePath(basename + ".ton");
matthiasm@310 2044
matthiasm@310 2045 cerr << path << endl;
matthiasm@310 2046
Chris@313 2047 // We don't want to overwrite an existing .ton file unless we put
Chris@313 2048 // it there in the first place
Chris@313 2049 bool shouldVerify = true;
Chris@313 2050 if (m_sessionFile == path) {
Chris@313 2051 shouldVerify = false;
Chris@313 2052 }
Chris@313 2053
Chris@313 2054 if (shouldVerify && QFileInfo(path).exists()) {
Chris@313 2055 if (QMessageBox::question(0, tr("File exists"),
Chris@313 2056 tr("<b>File exists</b><p>The file \"%1\" already exists.\nDo you want to overwrite it?").arg(path),
Chris@313 2057 QMessageBox::Ok,
Chris@313 2058 QMessageBox::Cancel) != QMessageBox::Ok) {
Chris@313 2059 return;
Chris@313 2060 }
Chris@313 2061 }
Chris@313 2062
Chris@314 2063 if (!waitForInitialAnalysis()) {
Chris@314 2064 QMessageBox::warning(this, tr("File not saved"),
Chris@314 2065 tr("Wait cancelled: the session has not been saved."));
Chris@314 2066 }
Chris@314 2067
matthiasm@310 2068 if (!saveSessionFile(path)) {
matthiasm@310 2069 QMessageBox::critical(this, tr("Failed to save file"),
matthiasm@310 2070 tr("Session file \"%1\" could not be saved.").arg(path));
matthiasm@310 2071 } else {
matthiasm@310 2072 setWindowTitle(tr("%1: %2")
matthiasm@310 2073 .arg(QApplication::applicationName())
matthiasm@310 2074 .arg(QFileInfo(path).fileName()));
matthiasm@310 2075 m_sessionFile = path;
matthiasm@310 2076 CommandHistory::getInstance()->documentSaved();
matthiasm@310 2077 documentRestored();
matthiasm@310 2078 m_recentFiles.addFile(path);
matthiasm@310 2079 }
matthiasm@310 2080 }
matthiasm@310 2081
matthiasm@310 2082 void
Chris@0 2083 MainWindow::saveSessionAs()
Chris@0 2084 {
Chris@270 2085 // We do not want to save mid-analysis regions -- that would cause
Chris@270 2086 // confusion on reloading
Chris@270 2087 m_analyser->clearReAnalysis();
Chris@270 2088 clearSelection();
Chris@270 2089
Chris@0 2090 QString path = getSaveFileName(FileFinder::SessionFile);
Chris@0 2091
Chris@309 2092 if (path == "") {
Chris@309 2093 return;
Chris@309 2094 }
Chris@0 2095
Chris@314 2096 if (!waitForInitialAnalysis()) {
Chris@314 2097 QMessageBox::warning(this, tr("File not saved"),
Chris@314 2098 tr("Wait cancelled: the session has not been saved."));
Chris@314 2099 return;
Chris@314 2100 }
Chris@314 2101
Chris@0 2102 if (!saveSessionFile(path)) {
Chris@85 2103 QMessageBox::critical(this, tr("Failed to save file"),
Chris@85 2104 tr("Session file \"%1\" could not be saved.").arg(path));
Chris@0 2105 } else {
Chris@85 2106 setWindowTitle(tr("%1: %2")
Chris@0 2107 .arg(QApplication::applicationName())
Chris@85 2108 .arg(QFileInfo(path).fileName()));
Chris@85 2109 m_sessionFile = path;
Chris@85 2110 CommandHistory::getInstance()->documentSaved();
Chris@85 2111 documentRestored();
Chris@0 2112 m_recentFiles.addFile(path);
Chris@0 2113 }
Chris@0 2114 }
Chris@0 2115
Chris@85 2116 QString
Chris@85 2117 MainWindow::exportToSVL(QString path, Layer *layer)
Chris@85 2118 {
Chris@573 2119 auto model = ModelById::get(layer->getModel());
Chris@573 2120 if (!model) return "Internal error: No model in layer";
Chris@85 2121
Chris@85 2122 QFile file(path);
Chris@85 2123 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
Chris@85 2124 return tr("Failed to open file %1 for writing").arg(path);
Chris@85 2125 } else {
Chris@85 2126 QTextStream out(&file);
Chris@85 2127 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
Chris@85 2128 << "<!DOCTYPE sonic-visualiser>\n"
Chris@85 2129 << "<sv>\n"
Chris@85 2130 << " <data>\n";
Chris@85 2131
Chris@85 2132 model->toXml(out, " ");
Chris@85 2133
Chris@85 2134 out << " </data>\n"
Chris@85 2135 << " <display>\n";
Chris@85 2136
Chris@85 2137 layer->toXml(out, " ");
Chris@85 2138
Chris@85 2139 out << " </display>\n"
Chris@85 2140 << "</sv>\n";
Chris@85 2141
Chris@85 2142 return "";
Chris@85 2143 }
Chris@85 2144 }
Chris@85 2145
Chris@0 2146 void
Chris@174 2147 MainWindow::importPitchLayer()
Chris@174 2148 {
Chris@174 2149 QString path = getOpenFileName(FileFinder::LayerFileNoMidiNonSV);
Chris@174 2150 if (path == "") return;
Chris@174 2151
Chris@174 2152 FileOpenStatus status = importPitchLayer(path);
Chris@174 2153
Chris@174 2154 if (status == FileOpenFailed) {
Chris@174 2155 emit hideSplash();
Chris@174 2156 QMessageBox::critical(this, tr("Failed to open file"),
Chris@174 2157 tr("<b>File open failed</b><p>Layer file %1 could not be opened.").arg(path));
Chris@174 2158 return;
Chris@174 2159 } else if (status == FileOpenWrongMode) {
Chris@174 2160 emit hideSplash();
Chris@174 2161 QMessageBox::critical(this, tr("Failed to open file"),
Chris@174 2162 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 2163 }
Chris@174 2164 }
Chris@174 2165
Chris@174 2166 MainWindow::FileOpenStatus
Chris@174 2167 MainWindow::importPitchLayer(FileSource source)
Chris@174 2168 {
Chris@174 2169 if (!source.isAvailable()) return FileOpenFailed;
Chris@174 2170 source.waitForData();
Chris@174 2171
Chris@442 2172 if (!waitForInitialAnalysis()) return FileOpenCancelled;
Chris@442 2173
Chris@174 2174 QString path = source.getLocalFilename();
Chris@174 2175
Chris@174 2176 RDFImporter::RDFDocumentType rdfType =
Chris@174 2177 RDFImporter::identifyDocumentType(QUrl::fromLocalFile(path).toString());
Chris@174 2178
Chris@174 2179 if (rdfType != RDFImporter::NotRDF) {
Chris@174 2180
Chris@174 2181 //!!!
Chris@174 2182 return FileOpenFailed;
Chris@174 2183
Chris@174 2184 } else if (source.getExtension().toLower() == "svl" ||
Chris@174 2185 (source.getExtension().toLower() == "xml" &&
Chris@174 2186 (SVFileReader::identifyXmlFile(source.getLocalFilename())
Chris@174 2187 == SVFileReader::SVLayerFile))) {
Chris@174 2188
Chris@174 2189 //!!!
Chris@174 2190 return FileOpenFailed;
Chris@174 2191
Chris@174 2192 } else {
Chris@174 2193
Chris@174 2194 try {
Chris@174 2195
Chris@174 2196 CSVFormat format(path);
Chris@174 2197 format.setSampleRate(getMainModel()->getSampleRate());
Chris@174 2198
Chris@174 2199 if (format.getModelType() != CSVFormat::TwoDimensionalModel) {
Chris@174 2200 //!!! error report
Chris@174 2201 return FileOpenFailed;
Chris@174 2202 }
Chris@174 2203
Chris@174 2204 Model *model = DataFileReaderFactory::loadCSV
Chris@174 2205 (path, format, getMainModel()->getSampleRate());
Chris@174 2206
Chris@174 2207 if (model) {
Chris@174 2208
Chris@174 2209 SVDEBUG << "MainWindow::importPitchLayer: Have model" << endl;
Chris@174 2210
Chris@573 2211 ModelId modelId = ModelById::add
Chris@573 2212 (std::shared_ptr<Model>(model));
Chris@573 2213
Chris@174 2214 CommandHistory::getInstance()->startCompoundOperation
Chris@174 2215 (tr("Import Pitch Track"), true);
Chris@174 2216
Chris@573 2217 Layer *newLayer = m_document->createImportedLayer(modelId);
Chris@174 2218
Chris@174 2219 m_analyser->takePitchTrackFrom(newLayer);
Chris@174 2220
Chris@174 2221 m_document->deleteLayer(newLayer);
Chris@174 2222
Chris@174 2223 CommandHistory::getInstance()->endCompoundOperation();
Chris@174 2224
Chris@174 2225 if (!source.isRemote()) {
Chris@174 2226 registerLastOpenedFilePath
Chris@174 2227 (FileFinder::LayerFile,
Chris@174 2228 path); // for file dialog
Chris@174 2229 }
Chris@174 2230
Chris@174 2231 return FileOpenSucceeded;
Chris@174 2232 }
Chris@174 2233 } catch (DataFileReaderFactory::Exception e) {
Chris@174 2234 if (e == DataFileReaderFactory::ImportCancelled) {
Chris@174 2235 return FileOpenCancelled;
Chris@174 2236 }
Chris@174 2237 }
Chris@174 2238 }
Chris@174 2239
Chris@174 2240 return FileOpenFailed;
Chris@174 2241 }
Chris@174 2242
Chris@174 2243 void
Chris@85 2244 MainWindow::exportPitchLayer()
matthiasm@26 2245 {
Chris@174 2246 Layer *layer = m_analyser->getLayer(Analyser::PitchTrack);
matthiasm@26 2247 if (!layer) return;
matthiasm@26 2248
Chris@573 2249 auto model = ModelById::getAs<SparseTimeValueModel>(layer->getModel());
matthiasm@26 2250 if (!model) return;
matthiasm@26 2251
Chris@96 2252 FileFinder::FileType type = FileFinder::LayerFileNoMidiNonSV;
matthiasm@26 2253
matthiasm@26 2254 QString path = getSaveFileName(type);
matthiasm@26 2255
matthiasm@26 2256 if (path == "") return;
matthiasm@26 2257
Chris@442 2258 if (!waitForInitialAnalysis()) return;
Chris@442 2259
matthiasm@26 2260 if (QFileInfo(path).suffix() == "") path += ".svl";
matthiasm@26 2261
matthiasm@26 2262 QString suffix = QFileInfo(path).suffix().toLower();
matthiasm@26 2263
matthiasm@26 2264 QString error;
matthiasm@26 2265
matthiasm@26 2266 if (suffix == "xml" || suffix == "svl") {
matthiasm@26 2267
Chris@85 2268 error = exportToSVL(path, layer);
matthiasm@26 2269
matthiasm@26 2270 } else if (suffix == "ttl" || suffix == "n3") {
matthiasm@26 2271
Chris@573 2272 RDFExporter exporter(path, model.get());
Chris@85 2273 exporter.write();
Chris@85 2274 if (!exporter.isOK()) {
Chris@85 2275 error = exporter.getError();
Chris@85 2276 }
Chris@85 2277
Chris@85 2278 } else {
Chris@85 2279
Chris@429 2280 DataExportOptions options = DataExportFillGaps;
Chris@429 2281
Chris@573 2282 CSVFileWriter writer(path, model.get(),
Chris@429 2283 ((suffix == "csv") ? "," : "\t"),
Chris@429 2284 options);
Chris@85 2285 writer.write();
Chris@85 2286
Chris@85 2287 if (!writer.isOK()) {
Chris@85 2288 error = writer.getError();
Chris@85 2289 }
Chris@85 2290 }
Chris@85 2291
Chris@85 2292 if (error != "") {
Chris@85 2293 QMessageBox::critical(this, tr("Failed to write file"), error);
Chris@85 2294 } else {
Chris@85 2295 emit activity(tr("Export layer to \"%1\"").arg(path));
Chris@85 2296 }
Chris@85 2297 }
Chris@85 2298
Chris@85 2299 void
Chris@85 2300 MainWindow::exportNoteLayer()
Chris@85 2301 {
Chris@174 2302 Layer *layer = m_analyser->getLayer(Analyser::Notes);
Chris@85 2303 if (!layer) return;
Chris@85 2304
Chris@573 2305 auto model = ModelById::getAs<NoteModel>(layer->getModel());
Chris@85 2306 if (!model) return;
Chris@85 2307
Chris@96 2308 FileFinder::FileType type = FileFinder::LayerFileNonSV;
Chris@85 2309
Chris@85 2310 QString path = getSaveFileName(type);
Chris@85 2311
Chris@85 2312 if (path == "") return;
Chris@85 2313
Chris@85 2314 if (QFileInfo(path).suffix() == "") path += ".svl";
Chris@85 2315
Chris@85 2316 QString suffix = QFileInfo(path).suffix().toLower();
Chris@85 2317
Chris@85 2318 QString error;
Chris@85 2319
Chris@85 2320 if (suffix == "xml" || suffix == "svl") {
Chris@85 2321
Chris@85 2322 error = exportToSVL(path, layer);
Chris@85 2323
Chris@85 2324 } else if (suffix == "mid" || suffix == "midi") {
Chris@85 2325
Chris@573 2326 MIDIFileWriter writer(path, model.get(), model->getSampleRate());
Chris@85 2327 writer.write();
Chris@85 2328 if (!writer.isOK()) {
Chris@85 2329 error = writer.getError();
Chris@85 2330 }
Chris@85 2331
Chris@85 2332 } else if (suffix == "ttl" || suffix == "n3") {
Chris@85 2333
Chris@573 2334 RDFExporter exporter(path, model.get());
Chris@85 2335 exporter.write();
Chris@85 2336 if (!exporter.isOK()) {
Chris@85 2337 error = exporter.getError();
matthiasm@26 2338 }
matthiasm@26 2339
matthiasm@26 2340 } else {
matthiasm@26 2341
Chris@429 2342 DataExportOptions options = DataExportOmitLevels;
Chris@429 2343
Chris@573 2344 CSVFileWriter writer(path, model.get(),
Chris@429 2345 ((suffix == "csv") ? "," : "\t"),
Chris@429 2346 options);
matthiasm@26 2347 writer.write();
matthiasm@26 2348
matthiasm@26 2349 if (!writer.isOK()) {
matthiasm@26 2350 error = writer.getError();
matthiasm@26 2351 }
matthiasm@26 2352 }
matthiasm@26 2353
matthiasm@26 2354 if (error != "") {
matthiasm@26 2355 QMessageBox::critical(this, tr("Failed to write file"), error);
matthiasm@26 2356 } else {
matthiasm@26 2357 emit activity(tr("Export layer to \"%1\"").arg(path));
matthiasm@26 2358 }
matthiasm@26 2359 }
matthiasm@26 2360
Chris@139 2361 void
Chris@485 2362 MainWindow::browseRecordedAudio()
Chris@485 2363 {
Chris@485 2364 if (!m_recordTarget) return;
Chris@485 2365
Chris@539 2366 QString path = RecordDirectory::getRecordContainerDirectory();
Chris@539 2367 if (path == "") path = RecordDirectory::getRecordDirectory();
Chris@485 2368 if (path == "") return;
Chris@485 2369
Chris@485 2370 openLocalFolder(path);
Chris@485 2371 }
Chris@485 2372
Chris@485 2373 void
Chris@399 2374 MainWindow::doubleClickSelectInvoked(sv_frame_t frame)
Chris@139 2375 {
Chris@399 2376 sv_frame_t f0, f1;
Chris@139 2377 m_analyser->getEnclosingSelectionScope(frame, f0, f1);
Chris@139 2378
Chris@139 2379 cerr << "MainWindow::doubleClickSelectInvoked(" << frame << "): [" << f0 << "," << f1 << "]" << endl;
Chris@139 2380
Chris@164 2381 Selection sel(f0, f1);
Chris@164 2382 m_viewManager->setSelection(sel);
Chris@164 2383 }
Chris@164 2384
Chris@164 2385 void
Chris@194 2386 MainWindow::abandonSelection()
Chris@167 2387 {
Chris@194 2388 // Named abandonSelection rather than clearSelection to indicate
Chris@194 2389 // that this is an active operation -- it restores the original
Chris@194 2390 // content of the pitch track in the selected region rather than
Chris@194 2391 // simply un-selecting.
Chris@194 2392
Chris@194 2393 cerr << "MainWindow::abandonSelection()" << endl;
Chris@167 2394
Chris@199 2395 CommandHistory::getInstance()->startCompoundOperation(tr("Abandon Selection"), true);
Chris@168 2396
Chris@194 2397 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@194 2398 if (!selections.empty()) {
Chris@194 2399 Selection sel = *selections.begin();
Chris@199 2400 m_analyser->abandonReAnalysis(sel);
Chris@252 2401 auxSnapNotes(sel);
Chris@194 2402 }
Chris@194 2403
Chris@167 2404 MainWindowBase::clearSelection();
Chris@168 2405
Chris@168 2406 CommandHistory::getInstance()->endCompoundOperation();
Chris@167 2407 }
Chris@167 2408
Chris@167 2409 void
Chris@192 2410 MainWindow::selectionChangedByUser()
Chris@164 2411 {
Chris@221 2412 if (!m_document) {
Chris@221 2413 // we're exiting, most likely
Chris@221 2414 return;
Chris@221 2415 }
Chris@221 2416
Chris@164 2417 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@164 2418
Chris@192 2419 cerr << "MainWindow::selectionChangedByUser" << endl;
Chris@192 2420
Chris@192 2421 m_analyser->showPitchCandidates(m_pendingConstraint.isConstrained());
Chris@190 2422
Chris@164 2423 if (!selections.empty()) {
Chris@164 2424 Selection sel = *selections.begin();
Chris@192 2425 cerr << "MainWindow::selectionChangedByUser: have selection" << endl;
Chris@192 2426 QString error = m_analyser->reAnalyseSelection
Chris@192 2427 (sel, m_pendingConstraint);
Chris@164 2428 if (error != "") {
Chris@164 2429 QMessageBox::critical
Chris@164 2430 (this, tr("Failed to analyse selection"),
Chris@164 2431 tr("<b>Analysis failed</b><p>%2</p>").arg(error));
Chris@164 2432 }
Chris@164 2433 }
Chris@192 2434
Chris@192 2435 m_pendingConstraint = Analyser::FrequencyRange();
Chris@192 2436 }
Chris@192 2437
Chris@192 2438 void
Chris@192 2439 MainWindow::regionOutlined(QRect r)
Chris@192 2440 {
Chris@192 2441 cerr << "MainWindow::regionOutlined(" << r.x() << "," << r.y() << "," << r.width() << "," << r.height() << ")" << endl;
Chris@192 2442
Chris@192 2443 Pane *pane = qobject_cast<Pane *>(sender());
Chris@192 2444 if (!pane) {
Chris@192 2445 cerr << "MainWindow::regionOutlined: not sent by pane, ignoring" << endl;
Chris@192 2446 return;
Chris@192 2447 }
Chris@192 2448
Chris@192 2449 if (!m_analyser) {
Chris@192 2450 cerr << "MainWindow::regionOutlined: no analyser, ignoring" << endl;
Chris@192 2451 return;
Chris@192 2452 }
Chris@192 2453
Chris@192 2454 SpectrogramLayer *spectrogram = qobject_cast<SpectrogramLayer *>
Chris@192 2455 (m_analyser->getLayer(Analyser::Spectrogram));
Chris@192 2456 if (!spectrogram) {
Chris@192 2457 cerr << "MainWindow::regionOutlined: no spectrogram layer, ignoring" << endl;
Chris@192 2458 return;
Chris@192 2459 }
Chris@192 2460
Chris@399 2461 sv_frame_t f0 = pane->getFrameForX(r.x());
Chris@399 2462 sv_frame_t f1 = pane->getFrameForX(r.x() + r.width());
Chris@192 2463
Chris@399 2464 double v0 = spectrogram->getFrequencyForY(pane, r.y() + r.height());
Chris@399 2465 double v1 = spectrogram->getFrequencyForY(pane, r.y());
Chris@192 2466
Chris@192 2467 cerr << "MainWindow::regionOutlined: frame " << f0 << " -> " << f1
Chris@192 2468 << ", frequency " << v0 << " -> " << v1 << endl;
Chris@192 2469
Chris@192 2470 m_pendingConstraint = Analyser::FrequencyRange(v0, v1);
Chris@192 2471
Chris@192 2472 Selection sel(f0, f1);
Chris@192 2473 m_viewManager->setSelection(sel);
Chris@0 2474 }
Chris@0 2475
Chris@0 2476 void
Chris@168 2477 MainWindow::clearPitches()
Chris@168 2478 {
Chris@168 2479 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@168 2480
Chris@168 2481 CommandHistory::getInstance()->startCompoundOperation(tr("Clear Pitches"), true);
Chris@168 2482
Chris@168 2483 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@168 2484 k != selections.end(); ++k) {
Chris@184 2485 m_analyser->deletePitches(*k);
Chris@252 2486 auxSnapNotes(*k);
Chris@168 2487 }
Chris@168 2488
Chris@168 2489 CommandHistory::getInstance()->endCompoundOperation();
Chris@168 2490 }
Chris@168 2491
Chris@168 2492 void
Chris@142 2493 MainWindow::octaveShift(bool up)
Chris@142 2494 {
Chris@142 2495 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@142 2496
Chris@211 2497 CommandHistory::getInstance()->startCompoundOperation
Chris@211 2498 (up ? tr("Choose Higher Octave") : tr("Choose Lower Octave"), true);
Chris@142 2499
Chris@168 2500 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@168 2501 k != selections.end(); ++k) {
Chris@142 2502
Chris@168 2503 m_analyser->shiftOctave(*k, up);
Chris@252 2504 auxSnapNotes(*k);
Chris@142 2505 }
Chris@142 2506
Chris@142 2507 CommandHistory::getInstance()->endCompoundOperation();
Chris@142 2508 }
Chris@142 2509
Chris@142 2510 void
Chris@184 2511 MainWindow::togglePitchCandidates()
Chris@184 2512 {
Chris@199 2513 CommandHistory::getInstance()->startCompoundOperation(tr("Toggle Pitch Candidates"), true);
Chris@199 2514
Chris@184 2515 m_analyser->showPitchCandidates(!m_analyser->arePitchCandidatesShown());
Chris@199 2516
Chris@199 2517 CommandHistory::getInstance()->endCompoundOperation();
Chris@199 2518
Chris@187 2519 updateMenuStates();
Chris@184 2520 }
Chris@184 2521
Chris@184 2522 void
Chris@184 2523 MainWindow::switchPitchUp()
Chris@167 2524 {
Chris@211 2525 if (m_analyser->arePitchCandidatesShown()) {
Chris@211 2526 if (m_analyser->haveHigherPitchCandidate()) {
Chris@211 2527
Chris@211 2528 CommandHistory::getInstance()->startCompoundOperation
Chris@211 2529 (tr("Choose Higher Pitch Candidate"), true);
Chris@211 2530
Chris@211 2531 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@211 2532
Chris@211 2533 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@211 2534 k != selections.end(); ++k) {
Chris@211 2535 m_analyser->switchPitchCandidate(*k, true);
Chris@252 2536 auxSnapNotes(*k);
Chris@211 2537 }
Chris@211 2538
Chris@211 2539 CommandHistory::getInstance()->endCompoundOperation();
Chris@211 2540 }
Chris@211 2541 } else {
Chris@211 2542 octaveShift(true);
Chris@167 2543 }
Chris@167 2544 }
Chris@167 2545
Chris@167 2546 void
Chris@184 2547 MainWindow::switchPitchDown()
Chris@184 2548 {
Chris@211 2549 if (m_analyser->arePitchCandidatesShown()) {
Chris@211 2550 if (m_analyser->haveLowerPitchCandidate()) {
Chris@211 2551
Chris@211 2552 CommandHistory::getInstance()->startCompoundOperation
Chris@211 2553 (tr("Choose Lower Pitch Candidate"), true);
Chris@211 2554
Chris@211 2555 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@211 2556
Chris@211 2557 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@211 2558 k != selections.end(); ++k) {
Chris@211 2559 m_analyser->switchPitchCandidate(*k, false);
Chris@252 2560 auxSnapNotes(*k);
Chris@211 2561 }
Chris@211 2562
Chris@211 2563 CommandHistory::getInstance()->endCompoundOperation();
Chris@211 2564 }
Chris@211 2565 } else {
Chris@211 2566 octaveShift(false);
Chris@184 2567 }
Chris@184 2568 }
Chris@184 2569
Chris@184 2570 void
Chris@237 2571 MainWindow::snapNotesToPitches()
Chris@237 2572 {
matthiasm@274 2573 cerr << "in snapNotesToPitches" << endl;
Chris@237 2574 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@237 2575
Chris@237 2576 if (!selections.empty()) {
Chris@237 2577
Chris@237 2578 CommandHistory::getInstance()->startCompoundOperation
Chris@237 2579 (tr("Snap Notes to Pitches"), true);
Chris@237 2580
Chris@237 2581 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@237 2582 k != selections.end(); ++k) {
Chris@239 2583 auxSnapNotes(*k);
Chris@237 2584 }
Chris@237 2585
Chris@237 2586 CommandHistory::getInstance()->endCompoundOperation();
Chris@237 2587 }
Chris@237 2588 }
Chris@237 2589
Chris@237 2590 void
Chris@239 2591 MainWindow::auxSnapNotes(Selection s)
Chris@239 2592 {
matthiasm@274 2593 cerr << "in auxSnapNotes" << endl;
Chris@239 2594 FlexiNoteLayer *layer =
Chris@239 2595 qobject_cast<FlexiNoteLayer *>(m_analyser->getLayer(Analyser::Notes));
Chris@239 2596 if (!layer) return;
Chris@239 2597
Chris@239 2598 layer->snapSelectedNotesToPitchTrack(m_analyser->getPane(), s);
Chris@239 2599 }
Chris@239 2600
Chris@239 2601 void
Chris@240 2602 MainWindow::splitNote()
Chris@237 2603 {
Chris@237 2604 FlexiNoteLayer *layer =
Chris@237 2605 qobject_cast<FlexiNoteLayer *>(m_analyser->getLayer(Analyser::Notes));
Chris@237 2606 if (!layer) return;
Chris@237 2607
Chris@240 2608 layer->splitNotesAt(m_analyser->getPane(), m_viewManager->getPlaybackFrame());
Chris@237 2609 }
Chris@237 2610
Chris@237 2611 void
Chris@238 2612 MainWindow::mergeNotes()
Chris@238 2613 {
Chris@238 2614 FlexiNoteLayer *layer =
Chris@238 2615 qobject_cast<FlexiNoteLayer *>(m_analyser->getLayer(Analyser::Notes));
Chris@238 2616 if (!layer) return;
Chris@238 2617
Chris@238 2618 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@238 2619
Chris@238 2620 if (!selections.empty()) {
Chris@238 2621
Chris@238 2622 CommandHistory::getInstance()->startCompoundOperation
Chris@238 2623 (tr("Merge Notes"), true);
Chris@238 2624
Chris@238 2625 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@238 2626 k != selections.end(); ++k) {
Chris@240 2627 layer->mergeNotes(m_analyser->getPane(), *k, true);
Chris@238 2628 }
Chris@238 2629
Chris@238 2630 CommandHistory::getInstance()->endCompoundOperation();
Chris@238 2631 }
Chris@238 2632 }
Chris@238 2633
Chris@238 2634 void
matthiasm@292 2635 MainWindow::deleteNotes()
matthiasm@292 2636 {
matthiasm@292 2637 FlexiNoteLayer *layer =
matthiasm@292 2638 qobject_cast<FlexiNoteLayer *>(m_analyser->getLayer(Analyser::Notes));
matthiasm@292 2639 if (!layer) return;
matthiasm@292 2640
matthiasm@292 2641 MultiSelection::SelectionList selections = m_viewManager->getSelections();
matthiasm@292 2642
matthiasm@292 2643 if (!selections.empty()) {
matthiasm@292 2644
matthiasm@292 2645 CommandHistory::getInstance()->startCompoundOperation
matthiasm@292 2646 (tr("Delete Notes"), true);
matthiasm@292 2647
matthiasm@292 2648 for (MultiSelection::SelectionList::iterator k = selections.begin();
matthiasm@292 2649 k != selections.end(); ++k) {
matthiasm@294 2650 layer->deleteSelectionInclusive(*k);
matthiasm@292 2651 }
matthiasm@292 2652
matthiasm@292 2653 CommandHistory::getInstance()->endCompoundOperation();
matthiasm@292 2654 }
matthiasm@292 2655 }
matthiasm@292 2656
matthiasm@292 2657
matthiasm@292 2658 void
Chris@240 2659 MainWindow::formNoteFromSelection()
Chris@240 2660 {
Chris@571 2661 Pane *pane = m_analyser->getPane();
matthiasm@293 2662 Layer *layer0 = m_analyser->getLayer(Analyser::Notes);
Chris@573 2663 auto model = ModelById::getAs<NoteModel>(layer0->getModel());
Chris@571 2664 FlexiNoteLayer *layer = qobject_cast<FlexiNoteLayer *>(layer0);
Chris@573 2665 if (!layer || !model) return;
Chris@240 2666
Chris@240 2667 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@240 2668
Chris@240 2669 if (!selections.empty()) {
Chris@240 2670
Chris@240 2671 CommandHistory::getInstance()->startCompoundOperation
Chris@240 2672 (tr("Form Note from Selection"), true);
Chris@571 2673
Chris@240 2674 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@240 2675 k != selections.end(); ++k) {
Chris@571 2676
Chris@571 2677 // Chop existing events at start and end frames; remember
Chris@571 2678 // the first starting pitch, to use as default for new
Chris@571 2679 // note; delete existing events; create new note; ask
Chris@571 2680 // layer to merge, just in order to adapt the note to the
Chris@571 2681 // existing pitch track if possible. This way we should
Chris@571 2682 // handle all the possible cases of existing notes that
Chris@571 2683 // may or may not overlap the start or end times
Chris@571 2684
Chris@571 2685 sv_frame_t start = k->getStartFrame();
Chris@571 2686 sv_frame_t end = k->getEndFrame();
Chris@571 2687
Chris@571 2688 EventVector existing =
Chris@571 2689 model->getEventsStartingWithin(start, end - start);
Chris@571 2690
Chris@571 2691 int defaultPitch = 100;
Chris@571 2692 if (!existing.empty()) {
Chris@571 2693 defaultPitch = int(roundf(existing.begin()->getValue()));
matthiasm@293 2694 }
Chris@571 2695
Chris@571 2696 layer->splitNotesAt(pane, start);
Chris@571 2697 layer->splitNotesAt(pane, end);
Chris@571 2698 layer->deleteSelection(*k);
Chris@571 2699
Chris@571 2700 layer->addNoteOn(start, defaultPitch, 100);
Chris@571 2701 layer->addNoteOff(end, defaultPitch);
Chris@571 2702
Chris@571 2703 layer->mergeNotes(pane, *k, false);
Chris@240 2704 }
Chris@240 2705
matthiasm@293 2706 CommandHistory::getInstance()->endCompoundOperation();
Chris@240 2707 }
Chris@240 2708 }
Chris@240 2709
Chris@240 2710 void
Chris@0 2711 MainWindow::playSpeedChanged(int position)
Chris@0 2712 {
Chris@474 2713 PlaySpeedRangeMapper mapper;
Chris@0 2714
Chris@399 2715 double percent = m_playSpeed->mappedValue();
Chris@399 2716 double factor = mapper.getFactorForValue(percent);
Chris@0 2717
Chris@474 2718 int centre = m_playSpeed->defaultValue();
Chris@474 2719
Chris@474 2720 // Percentage is shown to 0dp if >100, to 1dp if <100; factor is
Chris@474 2721 // shown to 3sf
Chris@474 2722
Chris@474 2723 char pcbuf[30];
Chris@474 2724 char facbuf[30];
Chris@474 2725
Chris@474 2726 if (position == centre) {
Chris@0 2727 contextHelpChanged(tr("Playback speed: Normal"));
Chris@474 2728 } else if (position < centre) {
Chris@474 2729 sprintf(pcbuf, "%.1f", percent);
Chris@474 2730 sprintf(facbuf, "%.3g", 1.0 / factor);
Chris@474 2731 contextHelpChanged(tr("Playback speed: %1% (%2x slower)")
Chris@474 2732 .arg(pcbuf)
Chris@474 2733 .arg(facbuf));
Chris@0 2734 } else {
Chris@474 2735 sprintf(pcbuf, "%.0f", percent);
Chris@474 2736 sprintf(facbuf, "%.3g", factor);
Chris@474 2737 contextHelpChanged(tr("Playback speed: %1% (%2x faster)")
Chris@474 2738 .arg(pcbuf)
Chris@474 2739 .arg(facbuf));
Chris@0 2740 }
Chris@0 2741
Chris@474 2742 m_playSource->setTimeStretch(1.0 / factor); // factor is a speedup
Chris@0 2743
Chris@0 2744 updateMenuStates();
Chris@0 2745 }
Chris@0 2746
Chris@0 2747 void
Chris@0 2748 MainWindow::playSharpenToggled()
Chris@0 2749 {
Chris@0 2750 QSettings settings;
Chris@0 2751 settings.beginGroup("MainWindow");
Chris@0 2752 settings.setValue("playsharpen", m_playSharpen->isChecked());
Chris@0 2753 settings.endGroup();
Chris@0 2754
Chris@0 2755 playSpeedChanged(m_playSpeed->value());
justin@157 2756 // TODO: pitch gain?
Chris@0 2757 }
Chris@0 2758
Chris@0 2759 void
Chris@0 2760 MainWindow::playMonoToggled()
Chris@0 2761 {
Chris@0 2762 QSettings settings;
Chris@0 2763 settings.beginGroup("MainWindow");
Chris@0 2764 settings.setValue("playmono", m_playMono->isChecked());
Chris@0 2765 settings.endGroup();
Chris@0 2766
Chris@0 2767 playSpeedChanged(m_playSpeed->value());
justin@157 2768 // TODO: pitch gain?
Chris@0 2769 }
Chris@0 2770
Chris@0 2771 void
Chris@0 2772 MainWindow::speedUpPlayback()
Chris@0 2773 {
Chris@0 2774 int value = m_playSpeed->value();
Chris@0 2775 value = value + m_playSpeed->pageStep();
Chris@0 2776 if (value > m_playSpeed->maximum()) value = m_playSpeed->maximum();
Chris@0 2777 m_playSpeed->setValue(value);
Chris@0 2778 }
Chris@0 2779
Chris@0 2780 void
Chris@0 2781 MainWindow::slowDownPlayback()
Chris@0 2782 {
Chris@0 2783 int value = m_playSpeed->value();
Chris@0 2784 value = value - m_playSpeed->pageStep();
Chris@0 2785 if (value < m_playSpeed->minimum()) value = m_playSpeed->minimum();
Chris@0 2786 m_playSpeed->setValue(value);
Chris@0 2787 }
Chris@0 2788
Chris@0 2789 void
Chris@0 2790 MainWindow::restoreNormalPlayback()
Chris@0 2791 {
Chris@0 2792 m_playSpeed->setValue(m_playSpeed->defaultValue());
Chris@0 2793 }
Chris@0 2794
justin@157 2795 void
Chris@404 2796 MainWindow::audioGainChanged(float gain)
justin@159 2797 {
Chris@404 2798 double db = AudioLevel::multiplier_to_dB(gain);
Chris@404 2799 cerr << "gain = " << gain << " (" << db << " dB)" << endl;
Chris@404 2800 contextHelpChanged(tr("Audio Gain: %1 dB").arg(db));
Chris@417 2801 if (gain == 0.f) {
Chris@417 2802 m_analyser->setAudible(Analyser::Audio, false);
Chris@417 2803 } else {
Chris@417 2804 m_analyser->setAudible(Analyser::Audio, true);
Chris@417 2805 m_analyser->setGain(Analyser::Audio, gain);
Chris@417 2806 }
justin@159 2807 updateMenuStates();
justin@159 2808 }
justin@159 2809
justin@159 2810 void
Chris@404 2811 MainWindow::pitchGainChanged(float gain)
justin@159 2812 {
Chris@404 2813 double db = AudioLevel::multiplier_to_dB(gain);
Chris@404 2814 cerr << "gain = " << gain << " (" << db << " dB)" << endl;
Chris@404 2815 contextHelpChanged(tr("Pitch Gain: %1 dB").arg(db));
Chris@417 2816 if (gain == 0.f) {
Chris@417 2817 m_analyser->setAudible(Analyser::PitchTrack, false);
Chris@417 2818 } else {
Chris@417 2819 m_analyser->setAudible(Analyser::PitchTrack, true);
Chris@417 2820 m_analyser->setGain(Analyser::PitchTrack, gain);
Chris@417 2821 }
justin@157 2822 updateMenuStates();
justin@157 2823 }
justin@157 2824
justin@157 2825 void
Chris@404 2826 MainWindow::notesGainChanged(float gain)
justin@157 2827 {
Chris@404 2828 double db = AudioLevel::multiplier_to_dB(gain);
Chris@404 2829 cerr << "gain = " << gain << " (" << db << " dB)" << endl;
Chris@404 2830 contextHelpChanged(tr("Notes Gain: %1 dB").arg(db));
Chris@417 2831 if (gain == 0.f) {
Chris@417 2832 m_analyser->setAudible(Analyser::Notes, false);
Chris@417 2833 } else {
Chris@417 2834 m_analyser->setAudible(Analyser::Notes, true);
Chris@417 2835 m_analyser->setGain(Analyser::Notes, gain);
Chris@417 2836 }
justin@159 2837 updateMenuStates();
justin@159 2838 }
justin@159 2839
justin@159 2840 void
Chris@404 2841 MainWindow::audioPanChanged(float pan)
justin@159 2842 {
Chris@404 2843 contextHelpChanged(tr("Audio Pan: %1").arg(pan));
Chris@404 2844 m_analyser->setPan(Analyser::Audio, pan);
justin@160 2845 updateMenuStates();
justin@160 2846 }
justin@160 2847
justin@160 2848 void
Chris@404 2849 MainWindow::pitchPanChanged(float pan)
justin@160 2850 {
Chris@404 2851 contextHelpChanged(tr("Pitch Pan: %1").arg(pan));
Chris@404 2852 m_analyser->setPan(Analyser::PitchTrack, pan);
justin@160 2853 updateMenuStates();
justin@160 2854 }
justin@160 2855
justin@160 2856 void
Chris@404 2857 MainWindow::notesPanChanged(float pan)
justin@160 2858 {
Chris@404 2859 contextHelpChanged(tr("Notes Pan: %1").arg(pan));
Chris@404 2860 m_analyser->setPan(Analyser::Notes, pan);
justin@160 2861 updateMenuStates();
justin@160 2862 }
justin@160 2863
justin@160 2864 void
Chris@0 2865 MainWindow::updateVisibleRangeDisplay(Pane *p) const
Chris@0 2866 {
Chris@0 2867 if (!getMainModel() || !p) {
Chris@0 2868 return;
Chris@0 2869 }
Chris@0 2870
Chris@0 2871 bool haveSelection = false;
Chris@403 2872 sv_frame_t startFrame = 0, endFrame = 0;
Chris@0 2873
Chris@0 2874 if (m_viewManager && m_viewManager->haveInProgressSelection()) {
Chris@0 2875
Chris@0 2876 bool exclusive = false;
Chris@0 2877 Selection s = m_viewManager->getInProgressSelection(exclusive);
Chris@0 2878
Chris@0 2879 if (!s.isEmpty()) {
Chris@0 2880 haveSelection = true;
Chris@0 2881 startFrame = s.getStartFrame();
Chris@0 2882 endFrame = s.getEndFrame();
Chris@0 2883 }
Chris@0 2884 }
Chris@0 2885
Chris@0 2886 if (!haveSelection) {
Chris@0 2887 startFrame = p->getFirstVisibleFrame();
Chris@0 2888 endFrame = p->getLastVisibleFrame();
Chris@0 2889 }
Chris@0 2890
Chris@0 2891 RealTime start = RealTime::frame2RealTime
Chris@0 2892 (startFrame, getMainModel()->getSampleRate());
Chris@0 2893
Chris@0 2894 RealTime end = RealTime::frame2RealTime
Chris@0 2895 (endFrame, getMainModel()->getSampleRate());
Chris@0 2896
Chris@0 2897 RealTime duration = end - start;
Chris@0 2898
Chris@0 2899 QString startStr, endStr, durationStr;
Chris@0 2900 startStr = start.toText(true).c_str();
Chris@0 2901 endStr = end.toText(true).c_str();
Chris@0 2902 durationStr = duration.toText(true).c_str();
Chris@0 2903
Chris@0 2904 if (haveSelection) {
Chris@0 2905 m_myStatusMessage = tr("Selection: %1 to %2 (duration %3)")
Chris@0 2906 .arg(startStr).arg(endStr).arg(durationStr);
Chris@0 2907 } else {
Chris@0 2908 m_myStatusMessage = tr("Visible: %1 to %2 (duration %3)")
Chris@0 2909 .arg(startStr).arg(endStr).arg(durationStr);
Chris@0 2910 }
matthiasm@42 2911
Chris@362 2912 getStatusLabel()->setText(m_myStatusMessage);
Chris@0 2913 }
Chris@0 2914
Chris@0 2915 void
Chris@0 2916 MainWindow::updatePositionStatusDisplays() const
Chris@0 2917 {
Chris@0 2918 if (!statusBar()->isVisible()) return;
Chris@0 2919
Chris@0 2920 }
Chris@0 2921
Chris@0 2922 void
Chris@521 2923 MainWindow::monitoringLevelsChanged(float left, float right)
Chris@0 2924 {
Chris@0 2925 m_fader->setPeakLeft(left);
Chris@0 2926 m_fader->setPeakRight(right);
Chris@0 2927 }
Chris@0 2928
Chris@0 2929 void
Chris@399 2930 MainWindow::sampleRateMismatch(sv_samplerate_t ,
Chris@399 2931 sv_samplerate_t ,
Chris@399 2932 bool )
Chris@0 2933 {
Chris@0 2934 updateDescriptionLabel();
Chris@0 2935 }
Chris@0 2936
Chris@0 2937 void
Chris@0 2938 MainWindow::audioOverloadPluginDisabled()
Chris@0 2939 {
Chris@0 2940 QMessageBox::information
Chris@0 2941 (this, tr("Audio processing overload"),
Chris@0 2942 tr("<b>Overloaded</b><p>Audio effects plugin auditioning has been disabled due to a processing overload."));
Chris@0 2943 }
Chris@0 2944
Chris@0 2945 void
Chris@0 2946 MainWindow::audioTimeStretchMultiChannelDisabled()
Chris@0 2947 {
Chris@0 2948 static bool shownOnce = false;
Chris@0 2949 if (shownOnce) return;
Chris@0 2950 QMessageBox::information
Chris@0 2951 (this, tr("Audio processing overload"),
Chris@0 2952 tr("<b>Overloaded</b><p>Audio playback speed processing has been reduced to a single channel, due to a processing overload."));
Chris@0 2953 shownOnce = true;
Chris@0 2954 }
Chris@0 2955
Chris@0 2956 void
Chris@0 2957 MainWindow::layerRemoved(Layer *layer)
Chris@0 2958 {
Chris@0 2959 MainWindowBase::layerRemoved(layer);
Chris@0 2960 }
Chris@0 2961
Chris@0 2962 void
Chris@0 2963 MainWindow::layerInAView(Layer *layer, bool inAView)
Chris@0 2964 {
Chris@0 2965 MainWindowBase::layerInAView(layer, inAView);
Chris@0 2966 }
Chris@0 2967
Chris@0 2968 void
Chris@573 2969 MainWindow::modelAdded(ModelId model)
Chris@0 2970 {
Chris@0 2971 MainWindowBase::modelAdded(model);
Chris@573 2972 auto dtvm = ModelById::getAs<DenseTimeValueModel>(model);
Chris@0 2973 if (dtvm) {
Chris@70 2974 cerr << "A dense time-value model (such as an audio file) has been loaded" << endl;
Chris@0 2975 }
Chris@0 2976 }
Chris@0 2977
Chris@0 2978 void
Chris@573 2979 MainWindow::mainModelChanged(ModelId model)
Chris@0 2980 {
Chris@0 2981 m_panLayer->setModel(model);
Chris@0 2982
Chris@0 2983 MainWindowBase::mainModelChanged(model);
Chris@0 2984
Chris@479 2985 if (m_playTarget || m_audioIO) {
Chris@0 2986 connect(m_fader, SIGNAL(valueChanged(float)),
Chris@474 2987 this, SLOT(mainModelGainChanged(float)));
Chris@474 2988 }
Chris@474 2989 }
Chris@474 2990
Chris@474 2991 void
Chris@474 2992 MainWindow::mainModelGainChanged(float gain)
Chris@474 2993 {
Chris@474 2994 if (m_playTarget) {
Chris@474 2995 m_playTarget->setOutputGain(gain);
Chris@479 2996 } else if (m_audioIO) {
Chris@479 2997 m_audioIO->setOutputGain(gain);
Chris@0 2998 }
Chris@259 2999 }
Chris@259 3000
Chris@259 3001 void
Chris@323 3002 MainWindow::analyseNow()
Chris@323 3003 {
Chris@323 3004 cerr << "analyseNow called" << endl;
Chris@325 3005 if (!m_analyser) return;
Chris@325 3006
Chris@325 3007 CommandHistory::getInstance()->startCompoundOperation
Chris@325 3008 (tr("Analyse Audio"), true);
Chris@325 3009
Chris@325 3010 QString error = m_analyser->analyseExistingFile();
Chris@325 3011
Chris@325 3012 CommandHistory::getInstance()->endCompoundOperation();
Chris@325 3013
Chris@325 3014 if (error != "") {
Chris@325 3015 QMessageBox::warning
Chris@325 3016 (this,
Chris@325 3017 tr("Failed to analyse audio"),
Chris@325 3018 tr("<b>Analysis failed</b><p>%1</p>").arg(error),
Chris@325 3019 QMessageBox::Ok);
Chris@325 3020 }
Chris@323 3021 }
Chris@323 3022
Chris@323 3023 void
Chris@259 3024 MainWindow::analyseNewMainModel()
Chris@259 3025 {
Chris@573 3026 auto model = getMainModel();
Chris@260 3027
Chris@604 3028 SVDEBUG << "MainWindow::analyseNewMainModel: main model is " << model << endl;
Chris@604 3029
Chris@604 3030 SVDEBUG << "(document is " << m_document << ", it says main model is " << m_document->getMainModel() << ")" << endl;
Chris@259 3031
Chris@324 3032 if (!model) {
Chris@324 3033 cerr << "no main model!" << endl;
Chris@324 3034 return;
Chris@324 3035 }
Chris@324 3036
Chris@324 3037 if (!m_paneStack) {
Chris@324 3038 cerr << "no pane stack!" << endl;
Chris@324 3039 return;
Chris@324 3040 }
Chris@324 3041
Chris@324 3042 int pc = m_paneStack->getPaneCount();
Chris@324 3043 Pane *pane = 0;
Chris@324 3044 Pane *selectionStrip = 0;
Chris@324 3045
Chris@324 3046 if (pc < 2) {
Chris@604 3047 SVDEBUG << "MainWindow::analyseNewMainModel: Adding pane and selection strip (ruler)" << endl;
Chris@604 3048 pane = m_paneStack->addPane();
Chris@604 3049 selectionStrip = m_paneStack->addPane();
Chris@324 3050 m_document->addLayerToView
Chris@324 3051 (selectionStrip,
Chris@324 3052 m_document->createMainModelLayer(LayerFactory::TimeRuler));
Chris@324 3053 } else {
Chris@324 3054 pane = m_paneStack->getPane(0);
Chris@324 3055 selectionStrip = m_paneStack->getPane(1);
Chris@324 3056 }
Chris@324 3057
Chris@362 3058 pane->setPlaybackFollow(PlaybackScrollPage);
Chris@362 3059
Chris@324 3060 if (selectionStrip) {
Chris@362 3061 selectionStrip->setPlaybackFollow(PlaybackScrollPage);
Chris@324 3062 selectionStrip->setFixedHeight(26);
Chris@324 3063 m_paneStack->sizePanesEqually();
Chris@324 3064 m_viewManager->clearToolModeOverrides();
Chris@324 3065 m_viewManager->setToolModeFor(selectionStrip,
Chris@324 3066 ViewManager::SelectMode);
Chris@324 3067 }
Chris@324 3068
Chris@324 3069 if (pane) {
Chris@324 3070
Chris@324 3071 disconnect(pane, SIGNAL(regionOutlined(QRect)),
Chris@324 3072 pane, SLOT(zoomToRegion(QRect)));
Chris@324 3073 connect(pane, SIGNAL(regionOutlined(QRect)),
Chris@324 3074 this, SLOT(regionOutlined(QRect)));
Chris@324 3075
Chris@324 3076 QString error = m_analyser->newFileLoaded
Chris@573 3077 (m_document, getMainModelId(), m_paneStack, pane);
Chris@324 3078 if (error != "") {
Chris@324 3079 QMessageBox::warning
Chris@324 3080 (this,
Chris@324 3081 tr("Failed to analyse audio"),
Chris@324 3082 tr("<b>Analysis failed</b><p>%1</p>").arg(error),
Chris@324 3083 QMessageBox::Ok);
Chris@72 3084 }
Chris@72 3085 }
matthiasm@366 3086
Chris@573 3087 if (!m_withSpectrogram) {
matthiasm@366 3088 m_analyser->setVisible(Analyser::Spectrogram, false);
matthiasm@366 3089 }
matthiasm@366 3090
Chris@573 3091 if (!m_withSonification) {
matthiasm@366 3092 m_analyser->setAudible(Analyser::PitchTrack, false);
matthiasm@366 3093 m_analyser->setAudible(Analyser::Notes, false);
matthiasm@366 3094 }
Chris@419 3095
Chris@419 3096 updateLayerStatuses();
Chris@487 3097 documentRestored();
Chris@0 3098 }
Chris@0 3099
Chris@0 3100 void
Chris@0 3101 MainWindow::modelGenerationFailed(QString transformName, QString message)
Chris@0 3102 {
Chris@0 3103 if (message != "") {
Chris@0 3104
Chris@0 3105 QMessageBox::warning
Chris@0 3106 (this,
Chris@0 3107 tr("Failed to generate layer"),
Chris@0 3108 tr("<b>Layer generation failed</b><p>Failed to generate derived layer.<p>The layer transform \"%1\" failed:<p>%2")
Chris@0 3109 .arg(transformName).arg(message),
Chris@0 3110 QMessageBox::Ok);
Chris@0 3111 } else {
Chris@0 3112 QMessageBox::warning
Chris@0 3113 (this,
Chris@0 3114 tr("Failed to generate layer"),
Chris@0 3115 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 3116 .arg(transformName),
Chris@0 3117 QMessageBox::Ok);
Chris@0 3118 }
Chris@0 3119 }
Chris@0 3120
Chris@0 3121 void
Chris@355 3122 MainWindow::modelGenerationWarning(QString /* transformName */, QString message)
Chris@0 3123 {
Chris@0 3124 QMessageBox::warning
Chris@0 3125 (this, tr("Warning"), message, QMessageBox::Ok);
Chris@0 3126 }
Chris@0 3127
Chris@0 3128 void
Chris@0 3129 MainWindow::modelRegenerationFailed(QString layerName,
Chris@399 3130 QString transformName,
Chris@399 3131 QString message)
Chris@0 3132 {
Chris@0 3133 if (message != "") {
Chris@0 3134
Chris@0 3135 QMessageBox::warning
Chris@0 3136 (this,
Chris@0 3137 tr("Failed to regenerate layer"),
Chris@0 3138 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 3139 .arg(layerName).arg(transformName).arg(message),
Chris@0 3140 QMessageBox::Ok);
Chris@0 3141 } else {
Chris@0 3142 QMessageBox::warning
Chris@0 3143 (this,
Chris@0 3144 tr("Failed to regenerate layer"),
Chris@0 3145 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 3146 .arg(layerName).arg(transformName),
Chris@0 3147 QMessageBox::Ok);
Chris@0 3148 }
Chris@0 3149 }
Chris@0 3150
Chris@0 3151 void
Chris@0 3152 MainWindow::modelRegenerationWarning(QString layerName,
Chris@355 3153 QString /* transformName */,
Chris@355 3154 QString message)
Chris@0 3155 {
Chris@0 3156 QMessageBox::warning
Chris@0 3157 (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 3158 }
Chris@0 3159
Chris@0 3160 void
Chris@520 3161 MainWindow::alignmentFailed(QString message)
Chris@0 3162 {
Chris@0 3163 QMessageBox::warning
Chris@0 3164 (this,
Chris@0 3165 tr("Failed to calculate alignment"),
Chris@520 3166 tr("<b>Alignment calculation failed</b><p>Failed to calculate an audio alignment:<p>%1")
Chris@520 3167 .arg(message),
Chris@0 3168 QMessageBox::Ok);
Chris@0 3169 }
Chris@0 3170
Chris@0 3171 void
Chris@0 3172 MainWindow::rightButtonMenuRequested(Pane *pane, QPoint position)
Chris@0 3173 {
Chris@70 3174 // cerr << "MainWindow::rightButtonMenuRequested(" << pane << ", " << position.x() << ", " << position.y() << ")" << endl;
Chris@0 3175 m_paneStack->setCurrentPane(pane);
Chris@0 3176 m_rightButtonMenu->popup(position);
Chris@0 3177 }
Chris@0 3178
Chris@0 3179 void
Chris@355 3180 MainWindow::handleOSCMessage(const OSCMessage &)
Chris@0 3181 {
Chris@70 3182 cerr << "MainWindow::handleOSCMessage: Not implemented" << endl;
Chris@0 3183 }
Chris@0 3184
Chris@0 3185 void
Chris@0 3186 MainWindow::mouseEnteredWidget()
Chris@0 3187 {
Chris@3 3188 QWidget *w = qobject_cast<QWidget *>(sender());
Chris@0 3189 if (!w) return;
Chris@0 3190
Chris@0 3191 if (w == m_fader) {
Chris@0 3192 contextHelpChanged(tr("Adjust the master playback level"));
Chris@0 3193 } else if (w == m_playSpeed) {
Chris@0 3194 contextHelpChanged(tr("Adjust the master playback speed"));
Chris@0 3195 } else if (w == m_playSharpen && w->isEnabled()) {
Chris@0 3196 contextHelpChanged(tr("Toggle transient sharpening for playback time scaling"));
Chris@0 3197 } else if (w == m_playMono && w->isEnabled()) {
Chris@0 3198 contextHelpChanged(tr("Toggle mono mode for playback time scaling"));
Chris@0 3199 }
Chris@0 3200 }
Chris@0 3201
Chris@0 3202 void
Chris@0 3203 MainWindow::mouseLeftWidget()
Chris@0 3204 {
Chris@0 3205 contextHelpChanged("");
Chris@0 3206 }
Chris@0 3207
Chris@0 3208 void
Chris@609 3209 MainWindow::betaReleaseWarning()
Chris@609 3210 {
Chris@609 3211 QMessageBox::information
Chris@609 3212 (this, tr("Beta release"),
Chris@609 3213 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 3214 }
Chris@609 3215
Chris@609 3216 void
Chris@0 3217 MainWindow::help()
Chris@0 3218 {
Chris@4 3219 //!!! todo: help URL!
matthiasm@369 3220 openHelpUrl(tr("http://code.soundsoftware.ac.uk/projects/tony/wiki/Reference"));
Chris@0 3221 }
Chris@0 3222
Chris@0 3223 void
Chris@585 3224 MainWindow::whatsNew()
Chris@585 3225 {
Chris@585 3226 QFile changelog(":CHANGELOG");
Chris@585 3227 changelog.open(QFile::ReadOnly);
Chris@585 3228 QByteArray content = changelog.readAll();
Chris@585 3229 QString text = QString::fromUtf8(content);
Chris@585 3230
Chris@585 3231 QDialog *d = new QDialog(this);
Chris@585 3232 d->setWindowTitle(tr("What's New"));
Chris@585 3233
Chris@585 3234 QGridLayout *layout = new QGridLayout;
Chris@585 3235 d->setLayout(layout);
Chris@585 3236
Chris@585 3237 int row = 0;
Chris@585 3238
Chris@585 3239 QLabel *iconLabel = new QLabel;
Chris@585 3240 iconLabel->setPixmap(QApplication::windowIcon().pixmap(64, 64));
Chris@585 3241 layout->addWidget(iconLabel, row, 0);
Chris@585 3242
Chris@585 3243 layout->addWidget
Chris@585 3244 (new QLabel(tr("<h3>What's New in %1</h3>")
Chris@585 3245 .arg(QApplication::applicationName())),
Chris@585 3246 row++, 1);
Chris@585 3247 layout->setColumnStretch(2, 10);
Chris@585 3248
Chris@585 3249 QTextEdit *textEdit = new QTextEdit;
Chris@585 3250 layout->addWidget(textEdit, row++, 1, 1, 2);
Chris@585 3251
Chris@585 3252 if (m_newerVersionIs != "") {
Chris@585 3253 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 3254 }
Chris@585 3255
Chris@585 3256 QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Ok);
Chris@585 3257 layout->addWidget(bb, row++, 0, 1, 3);
Chris@585 3258 connect(bb, SIGNAL(accepted()), d, SLOT(accept()));
Chris@585 3259
Chris@585 3260 text.replace('\r', "");
Chris@585 3261 text.replace(QRegExp("(.)\n +(.)"), "\\1 \\2");
Chris@585 3262 text.replace(QRegExp("\n - ([^\n]+)"), "\n<li>\\1</li>");
Chris@585 3263 text.replace(QRegExp(": *\n"), ":\n<ul>\n");
Chris@585 3264 text.replace(QRegExp("</li>\n\\s*\n"), "</li>\n</ul>\n\n");
Chris@585 3265 text.replace(QRegExp("\n(\\w[^:\n]+:)"), "\n<p><b>\\1</b></p>");
Chris@585 3266 // text.replace(QRegExp("<li>([^,.\n]+)([,.] +\\w)"), "<li><b>\\1</b>\\2");
Chris@585 3267
Chris@585 3268 textEdit->setHtml(text);
Chris@585 3269 textEdit->setReadOnly(true);
Chris@585 3270
Chris@585 3271 d->setMinimumSize(m_viewManager->scalePixelSize(520),
Chris@585 3272 m_viewManager->scalePixelSize(450));
Chris@585 3273
Chris@585 3274 d->exec();
Chris@585 3275
Chris@585 3276 delete d;
Chris@585 3277 }
Chris@585 3278
Chris@598 3279 QString
Chris@598 3280 MainWindow::getReleaseText() const
Chris@598 3281 {
Chris@598 3282 bool debug = false;
Chris@598 3283 QString version = "(unknown version)";
Chris@598 3284
Chris@598 3285 #ifdef BUILD_DEBUG
Chris@598 3286 debug = true;
Chris@598 3287 #endif // BUILD_DEBUG
Chris@598 3288 #ifdef TONY_VERSION
Chris@598 3289 #ifdef SVNREV
Chris@598 3290 version = tr("Release %1 : Revision %2").arg(TONY_VERSION).arg(SVNREV);
Chris@598 3291 #else // !SVNREV
Chris@598 3292 version = tr("Release %1").arg(TONY_VERSION);
Chris@598 3293 #endif // SVNREV
Chris@598 3294 #else // !TONY_VERSION
Chris@598 3295 #ifdef SVNREV
Chris@598 3296 version = tr("Unreleased : Revision %1").arg(SVNREV);
Chris@598 3297 #endif // SVNREV
Chris@598 3298 #endif // TONY_VERSION
Chris@598 3299
Chris@598 3300 return tr("%1 : %2 configuration, %3-bit build")
Chris@598 3301 .arg(version)
Chris@598 3302 .arg(debug ? tr("Debug") : tr("Release"))
Chris@598 3303 .arg(sizeof(void *) * 8);
Chris@598 3304 }
Chris@598 3305
Chris@585 3306 void
Chris@0 3307 MainWindow::about()
Chris@0 3308 {
Chris@0 3309 QString aboutText;
Chris@0 3310
Chris@0 3311 aboutText += tr("<h3>About Tony</h3>");
Chris@0 3312 aboutText += tr("<p>Tony is a program for interactive note and pitch analysis and annotation.</p>");
Chris@598 3313 aboutText += QString("<p><small>%1</small></p>").arg(getReleaseText());
Chris@212 3314 aboutText += tr("<p>Using Qt framework version %1.</p>")
Chris@206 3315 .arg(QT_VERSION_STR);
Chris@0 3316
Chris@0 3317 aboutText +=
Chris@585 3318 "<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 3319 "<p>pYIN analysis plugin written by Matthias Mauch.</p>"
Chris@90 3320 "<p>This program is free software; you can redistribute it and/or "
Chris@90 3321 "modify it under the terms of the GNU General Public License as "
Chris@90 3322 "published by the Free Software Foundation; either version 2 of the "
Chris@0 3323 "License, or (at your option) any later version.<br>See the file "
Chris@0 3324 "COPYING included with this distribution for more information.</p>";
Chris@0 3325
Chris@585 3326 // use our own dialog so we can influence the size
Chris@585 3327
Chris@585 3328 QDialog *d = new QDialog(this);
Chris@585 3329
Chris@585 3330 d->setWindowTitle(tr("About %1").arg(QApplication::applicationName()));
Chris@585 3331
Chris@585 3332 QGridLayout *layout = new QGridLayout;
Chris@585 3333 d->setLayout(layout);
Chris@585 3334
Chris@585 3335 int row = 0;
Chris@585 3336
Chris@585 3337 QLabel *iconLabel = new QLabel;
Chris@585 3338 iconLabel->setPixmap(QApplication::windowIcon().pixmap(64, 64));
Chris@585 3339 layout->addWidget(iconLabel, row, 0, Qt::AlignTop);
Chris@585 3340
Chris@585 3341 QLabel *mainText = new QLabel();
Chris@585 3342 layout->addWidget(mainText, row, 1, 1, 2);
Chris@585 3343
Chris@585 3344 layout->setRowStretch(row, 10);
Chris@585 3345 layout->setColumnStretch(1, 10);
Chris@585 3346
Chris@585 3347 ++row;
Chris@585 3348
Chris@585 3349 QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Ok);
Chris@585 3350 layout->addWidget(bb, row++, 0, 1, 3);
Chris@585 3351 connect(bb, SIGNAL(accepted()), d, SLOT(accept()));
Chris@585 3352
Chris@585 3353 mainText->setWordWrap(true);
Chris@585 3354 mainText->setOpenExternalLinks(true);
Chris@585 3355 mainText->setText(aboutText);
Chris@585 3356
Chris@585 3357 d->setMinimumSize(m_viewManager->scalePixelSize(420),
Chris@585 3358 m_viewManager->scalePixelSize(200));
Chris@585 3359
Chris@585 3360 d->exec();
Chris@585 3361
Chris@585 3362 delete d;
Chris@0 3363 }
Chris@0 3364
Chris@0 3365 void
Chris@0 3366 MainWindow::keyReference()
Chris@0 3367 {
Chris@0 3368 m_keyReference->show();
Chris@0 3369 }
Chris@0 3370
Chris@231 3371 void
Chris@231 3372 MainWindow::newerVersionAvailable(QString version)
Chris@231 3373 {
Chris@585 3374 m_newerVersionIs = version;
Chris@585 3375
Chris@231 3376 //!!! nicer URL would be nicer
Chris@231 3377 QSettings settings;
Chris@231 3378 settings.beginGroup("NewerVersionWarning");
Chris@231 3379 QString tag = QString("version-%1-available-show").arg(version);
Chris@231 3380 if (settings.value(tag, true).toBool()) {
Chris@231 3381 QString title(tr("Newer version available"));
Chris@231 3382 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 3383 QMessageBox::information(this, title, text);
Chris@231 3384 settings.setValue(tag, false);
Chris@231 3385 }
Chris@231 3386 settings.endGroup();
Chris@231 3387 }
Chris@231 3388
matthiasm@356 3389 void
matthiasm@356 3390 MainWindow::ffwd()
matthiasm@356 3391 {
matthiasm@356 3392 if (!getMainModel()) return;
matthiasm@356 3393
Chris@403 3394 sv_frame_t frame = m_viewManager->getPlaybackFrame();
matthiasm@356 3395 ++frame;
matthiasm@356 3396
Chris@403 3397 sv_samplerate_t sr = getMainModel()->getSampleRate();
matthiasm@356 3398
matthiasm@356 3399 // The step is supposed to scale and be as wide as a step of
matthiasm@357 3400 // m_defaultFfwdRwdStep seconds at zoom level 720 and sr = 44100
Chris@540 3401
Chris@540 3402 ZoomLevel zoom = m_viewManager->getGlobalZoom();
Chris@540 3403 double framesPerPixel = 1.0;
Chris@540 3404 if (zoom.zone == ZoomLevel::FramesPerPixel) {
Chris@540 3405 framesPerPixel = zoom.level;
Chris@540 3406 } else {
Chris@540 3407 framesPerPixel = 1.0 / zoom.level;
Chris@540 3408 }
Chris@540 3409 double defaultFramesPerPixel = (720 * 44100) / sr;
Chris@540 3410 double scaler = framesPerPixel / defaultFramesPerPixel;
Chris@403 3411 RealTime step = m_defaultFfwdRwdStep * scaler;
Chris@403 3412
matthiasm@356 3413 frame = RealTime::realTime2Frame
Chris@403 3414 (RealTime::frame2RealTime(frame, sr) + step, sr);
Chris@403 3415
Chris@403 3416 if (frame > getMainModel()->getEndFrame()) {
matthiasm@356 3417 frame = getMainModel()->getEndFrame();
matthiasm@356 3418 }
Chris@403 3419
matthiasm@356 3420 if (frame < 0) frame = 0;
matthiasm@356 3421
matthiasm@356 3422 if (m_viewManager->getPlaySelectionMode()) {
Chris@403 3423 frame = m_viewManager->constrainFrameToSelection(frame);
matthiasm@356 3424 }
matthiasm@356 3425
matthiasm@356 3426 m_viewManager->setPlaybackFrame(frame);
matthiasm@356 3427
Chris@403 3428 if (frame == getMainModel()->getEndFrame() &&
matthiasm@356 3429 m_playSource &&
matthiasm@356 3430 m_playSource->isPlaying() &&
matthiasm@356 3431 !m_viewManager->getPlayLoopMode()) {
matthiasm@356 3432 stop();
matthiasm@356 3433 }
matthiasm@356 3434 }
matthiasm@356 3435
matthiasm@356 3436 void
matthiasm@356 3437 MainWindow::rewind()
matthiasm@356 3438 {
matthiasm@356 3439 if (!getMainModel()) return;
matthiasm@356 3440
Chris@403 3441 sv_frame_t frame = m_viewManager->getPlaybackFrame();
matthiasm@356 3442 if (frame > 0) --frame;
matthiasm@356 3443
Chris@403 3444 sv_samplerate_t sr = getMainModel()->getSampleRate();
matthiasm@356 3445
matthiasm@356 3446 // The step is supposed to scale and be as wide as a step of
matthiasm@357 3447 // m_defaultFfwdRwdStep seconds at zoom level 720 and sr = 44100
Chris@540 3448
Chris@540 3449 ZoomLevel zoom = m_viewManager->getGlobalZoom();
Chris@540 3450 double framesPerPixel = 1.0;
Chris@540 3451 if (zoom.zone == ZoomLevel::FramesPerPixel) {
Chris@540 3452 framesPerPixel = zoom.level;
Chris@540 3453 } else {
Chris@540 3454 framesPerPixel = 1.0 / zoom.level;
Chris@540 3455 }
Chris@540 3456 double defaultFramesPerPixel = (720 * 44100) / sr;
Chris@540 3457 double scaler = framesPerPixel / defaultFramesPerPixel;
Chris@403 3458 RealTime step = m_defaultFfwdRwdStep * scaler;
Chris@403 3459
matthiasm@356 3460 frame = RealTime::realTime2Frame
Chris@403 3461 (RealTime::frame2RealTime(frame, sr) - step, sr);
Chris@403 3462
Chris@403 3463 if (frame < getMainModel()->getStartFrame()) {
matthiasm@356 3464 frame = getMainModel()->getStartFrame();
matthiasm@356 3465 }
matthiasm@356 3466
matthiasm@356 3467 if (frame < 0) frame = 0;
matthiasm@356 3468
matthiasm@356 3469 if (m_viewManager->getPlaySelectionMode()) {
Chris@403 3470 frame = m_viewManager->constrainFrameToSelection(frame);
matthiasm@356 3471 }
matthiasm@356 3472
matthiasm@356 3473 m_viewManager->setPlaybackFrame(frame);
matthiasm@356 3474 }