annotate main/MainWindow.cpp @ 609:ef0778016c88

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