annotate main/MainWindow.cpp @ 586:1dfdbd017cf5

Suppress property boxes. They aren't visible anyway, but updates to their invisible state seems to be causing problems with certain Qt builds on Windows.
author Chris Cannam
date Wed, 04 Sep 2019 14:19:57 +0100
parents d882f64e60db
children 7079871e62c9
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@474 99 MainWindowBase(options),
Chris@0 100 m_overview(0),
Chris@0 101 m_mainMenusCreated(false),
Chris@0 102 m_playbackMenu(0),
gyorgyf@45 103 m_recentFilesMenu(0),
Chris@0 104 m_rightButtonMenu(0),
Chris@0 105 m_rightButtonPlaybackMenu(0),
Chris@0 106 m_deleteSelectedAction(0),
Chris@0 107 m_ffwdAction(0),
Chris@0 108 m_rwdAction(0),
Chris@168 109 m_intelligentActionOn(true), //GF: !!! temporary
Chris@244 110 m_activityLog(new ActivityLog()),
matthiasm@296 111 m_keyReference(new KeyReference()),
matthiasm@364 112 m_selectionAnchor(0),
matthiasm@364 113 m_withSonification(withSonification),
matthiasm@364 114 m_withSpectrogram(withSpectrogram)
Chris@0 115 {
Chris@0 116 setWindowTitle(QApplication::applicationName());
Chris@0 117
Chris@206 118 #ifdef Q_OS_MAC
Chris@206 119 #if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
Chris@206 120 setUnifiedTitleAndToolBarOnMac(true);
Chris@206 121 #endif
Chris@206 122 #endif
Chris@206 123
Chris@0 124 UnitDatabase *udb = UnitDatabase::getInstance();
Chris@0 125 udb->registerUnit("Hz");
Chris@0 126 udb->registerUnit("dB");
Chris@0 127 udb->registerUnit("s");
Chris@0 128
Chris@0 129 ColourDatabase *cdb = ColourDatabase::getInstance();
Chris@0 130 cdb->addColour(Qt::black, tr("Black"));
Chris@0 131 cdb->addColour(Qt::darkRed, tr("Red"));
Chris@0 132 cdb->addColour(Qt::darkBlue, tr("Blue"));
Chris@0 133 cdb->addColour(Qt::darkGreen, tr("Green"));
Chris@0 134 cdb->addColour(QColor(200, 50, 255), tr("Purple"));
Chris@0 135 cdb->addColour(QColor(255, 150, 50), tr("Orange"));
Chris@120 136 cdb->addColour(QColor(180, 180, 180), tr("Grey"));
Chris@0 137 cdb->setUseDarkBackground(cdb->addColour(Qt::white, tr("White")), true);
Chris@0 138 cdb->setUseDarkBackground(cdb->addColour(Qt::red, tr("Bright Red")), true);
Chris@0 139 cdb->setUseDarkBackground(cdb->addColour(QColor(30, 150, 255), tr("Bright Blue")), true);
Chris@0 140 cdb->setUseDarkBackground(cdb->addColour(Qt::green, tr("Bright Green")), true);
Chris@0 141 cdb->setUseDarkBackground(cdb->addColour(QColor(225, 74, 255), tr("Bright Purple")), true);
Chris@0 142 cdb->setUseDarkBackground(cdb->addColour(QColor(255, 188, 80), tr("Bright Orange")), true);
Chris@0 143
Chris@0 144 Preferences::getInstance()->setResampleOnLoad(true);
Chris@315 145 Preferences::getInstance()->setFixedSampleRate(44100);
Chris@0 146 Preferences::getInstance()->setSpectrogramSmoothing
Chris@0 147 (Preferences::SpectrogramInterpolated);
Chris@332 148 Preferences::getInstance()->setNormaliseAudio(true);
Chris@0 149
Chris@0 150 QSettings settings;
Chris@0 151
Chris@0 152 settings.beginGroup("MainWindow");
Chris@0 153 settings.setValue("showstatusbar", false);
Chris@0 154 settings.endGroup();
Chris@0 155
Chris@96 156 settings.beginGroup("Transformer");
Chris@96 157 settings.setValue("use-flexi-note-model", true);
Chris@96 158 settings.endGroup();
Chris@96 159
Chris@73 160 settings.beginGroup("LayerDefaults");
Chris@73 161 settings.setValue("waveform",
Chris@73 162 QString("<layer scale=\"%1\" channelMode=\"%2\"/>")
Chris@73 163 .arg(int(WaveformLayer::LinearScale))
Chris@73 164 .arg(int(WaveformLayer::MixChannels)));
Chris@73 165 settings.endGroup();
Chris@73 166
Chris@6 167 m_viewManager->setAlignMode(false);
Chris@6 168 m_viewManager->setPlaySoloMode(false);
Chris@392 169 m_viewManager->setToolMode(ViewManager::NavigateMode);
Chris@0 170 m_viewManager->setZoomWheelsEnabled(false);
Chris@6 171 m_viewManager->setIlluminateLocalFeatures(true);
matthiasm@368 172 m_viewManager->setShowWorkTitle(false);
Chris@6 173 m_viewManager->setShowCentreLine(false);
matthiasm@368 174 m_viewManager->setShowDuration(false);
Chris@213 175 m_viewManager->setOverlayMode(ViewManager::GlobalOverlays);
Chris@0 176
Chris@192 177 connect(m_viewManager, SIGNAL(selectionChangedByUser()),
Chris@192 178 this, SLOT(selectionChangedByUser()));
Chris@164 179
Chris@0 180 QFrame *frame = new QFrame;
Chris@0 181 setCentralWidget(frame);
Chris@0 182
Chris@0 183 QGridLayout *layout = new QGridLayout;
Chris@0 184
Chris@0 185 QScrollArea *scroll = new QScrollArea(frame);
Chris@0 186 scroll->setWidgetResizable(true);
Chris@0 187 scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
Chris@0 188 scroll->setFrameShape(QFrame::NoFrame);
Chris@0 189
Chris@6 190 // We have a pane stack: it comes with the territory. However, we
Chris@120 191 // have a fixed and known number of panes in it -- it isn't
Chris@120 192 // variable
Chris@0 193 m_paneStack->setLayoutStyle(PaneStack::NoPropertyStacks);
Chris@137 194 m_paneStack->setShowPaneAccessories(false);
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@0 1267 }
Chris@0 1268
matthiasm@281 1269
matthiasm@281 1270 void
matthiasm@281 1271 MainWindow::moveOneNoteRight()
matthiasm@281 1272 {
matthiasm@281 1273 // cerr << "MainWindow::moveOneNoteRight" << endl;
matthiasm@296 1274 moveByOneNote(true, false);
matthiasm@281 1275 }
matthiasm@281 1276
matthiasm@281 1277 void
matthiasm@281 1278 MainWindow::moveOneNoteLeft()
matthiasm@281 1279 {
matthiasm@281 1280 // cerr << "MainWindow::moveOneNoteLeft" << endl;
matthiasm@296 1281 moveByOneNote(false, false);
matthiasm@281 1282 }
matthiasm@281 1283
matthiasm@281 1284 void
matthiasm@283 1285 MainWindow::selectOneNoteRight()
matthiasm@283 1286 {
matthiasm@296 1287 moveByOneNote(true, true);
matthiasm@283 1288 }
matthiasm@283 1289
matthiasm@283 1290 void
matthiasm@283 1291 MainWindow::selectOneNoteLeft()
matthiasm@283 1292 {
matthiasm@296 1293 moveByOneNote(false, true);
matthiasm@283 1294 }
matthiasm@283 1295
matthiasm@283 1296
matthiasm@283 1297 void
matthiasm@296 1298 MainWindow::moveByOneNote(bool right, bool doSelect)
matthiasm@281 1299 {
Chris@399 1300 sv_frame_t frame = m_viewManager->getPlaybackFrame();
matthiasm@301 1301 cerr << "MainWindow::moveByOneNote startframe: " << frame << endl;
matthiasm@281 1302
matthiasm@304 1303 bool isAtSelectionBoundary = false;
matthiasm@304 1304 MultiSelection::SelectionList selections = m_viewManager->getSelections();
matthiasm@304 1305 if (!selections.empty()) {
matthiasm@304 1306 Selection sel = *selections.begin();
matthiasm@342 1307 isAtSelectionBoundary = (frame == sel.getStartFrame()) || (frame == sel.getEndFrame());
matthiasm@304 1308 }
matthiasm@304 1309 if (!doSelect || !isAtSelectionBoundary) {
matthiasm@296 1310 m_selectionAnchor = frame;
matthiasm@296 1311 }
matthiasm@296 1312
matthiasm@281 1313 Layer *layer = m_analyser->getLayer(Analyser::Notes);
matthiasm@281 1314 if (!layer) return;
matthiasm@281 1315
Chris@573 1316 auto model = ModelById::getAs<NoteModel>(layer->getModel());
matthiasm@281 1317 if (!model) return;
matthiasm@281 1318
Chris@570 1319 //!!! This seems like a strange and inefficient way to do this -
Chris@570 1320 //!!! there is almost certainly a better way making use of
Chris@570 1321 //!!! EventSeries api
Chris@570 1322
Chris@570 1323 EventVector points = model->getAllEvents();
matthiasm@281 1324 if (points.empty()) return;
matthiasm@281 1325
Chris@570 1326 EventVector::iterator i = points.begin();
Chris@399 1327 std::set<sv_frame_t> snapFrames;
matthiasm@281 1328 snapFrames.insert(0);
matthiasm@281 1329 while (i != points.end()) {
Chris@570 1330 snapFrames.insert(i->getFrame());
Chris@570 1331 snapFrames.insert(i->getFrame() + i->getDuration() + 1);
matthiasm@281 1332 ++i;
matthiasm@281 1333 }
Chris@399 1334 std::set<sv_frame_t>::iterator i2;
Chris@399 1335 if (snapFrames.find(frame) == snapFrames.end()) {
matthiasm@281 1336 // we're not on an existing snap point, so go to previous
matthiasm@281 1337 snapFrames.insert(frame);
matthiasm@281 1338 }
matthiasm@281 1339 i2 = snapFrames.find(frame);
Chris@399 1340 if (right) {
matthiasm@281 1341 i2++;
matthiasm@281 1342 if (i2 == snapFrames.end()) i2--;
matthiasm@281 1343 } else {
matthiasm@281 1344 if (i2 != snapFrames.begin()) i2--;
matthiasm@281 1345 }
matthiasm@281 1346 frame = *i2;
matthiasm@281 1347 m_viewManager->setPlaybackFrame(frame);
matthiasm@296 1348 if (doSelect) {
matthiasm@296 1349 Selection sel;
matthiasm@296 1350 if (frame > m_selectionAnchor) {
matthiasm@342 1351 sel = Selection(m_selectionAnchor, frame);
matthiasm@296 1352 } else {
matthiasm@342 1353 sel = Selection(frame, m_selectionAnchor);
matthiasm@296 1354 }
matthiasm@296 1355 m_viewManager->setSelection(sel);
matthiasm@296 1356 }
matthiasm@301 1357 cerr << "MainWindow::moveByOneNote endframe: " << frame << endl;
matthiasm@281 1358 }
matthiasm@281 1359
Chris@0 1360 void
Chris@70 1361 MainWindow::toolNavigateSelected()
Chris@70 1362 {
Chris@70 1363 m_viewManager->setToolMode(ViewManager::NavigateMode);
Chris@70 1364 m_intelligentActionOn = true;
Chris@70 1365 }
Chris@70 1366
Chris@70 1367 void
Chris@70 1368 MainWindow::toolEditSelected()
Chris@70 1369 {
matthiasm@294 1370 cerr << "MainWindow::toolEditSelected" << endl;
Chris@77 1371 m_viewManager->setToolMode(ViewManager::NoteEditMode);
Chris@70 1372 m_intelligentActionOn = true;
Chris@70 1373 m_analyser->setIntelligentActions(m_intelligentActionOn);
Chris@70 1374 }
Chris@70 1375
Chris@70 1376 void
Chris@70 1377 MainWindow::toolFreeEditSelected()
Chris@70 1378 {
Chris@77 1379 m_viewManager->setToolMode(ViewManager::NoteEditMode);
Chris@70 1380 m_intelligentActionOn = false;
Chris@70 1381 m_analyser->setIntelligentActions(m_intelligentActionOn);
Chris@70 1382 }
Chris@70 1383
gyorgyf@21 1384 void
Chris@0 1385 MainWindow::updateMenuStates()
Chris@0 1386 {
Chris@0 1387 MainWindowBase::updateMenuStates();
Chris@0 1388
Chris@0 1389 Pane *currentPane = 0;
Chris@0 1390 Layer *currentLayer = 0;
Chris@0 1391
Chris@0 1392 if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
Chris@0 1393 if (currentPane) currentLayer = currentPane->getSelectedLayer();
Chris@0 1394
Chris@291 1395 bool haveMainModel =
Chris@291 1396 (getMainModel() != 0);
Chris@291 1397 bool havePlayTarget =
Chris@479 1398 (m_playTarget != 0 || m_audioIO != 0);
Chris@0 1399 bool haveCurrentPane =
Chris@0 1400 (currentPane != 0);
Chris@0 1401 bool haveCurrentLayer =
Chris@0 1402 (haveCurrentPane &&
Chris@0 1403 (currentLayer != 0));
Chris@0 1404 bool haveSelection =
Chris@187 1405 (m_viewManager &&
Chris@187 1406 !m_viewManager->getSelections().empty());
Chris@0 1407 bool haveCurrentTimeInstantsLayer =
Chris@187 1408 (haveCurrentLayer &&
Chris@187 1409 qobject_cast<TimeInstantLayer *>(currentLayer));
Chris@0 1410 bool haveCurrentTimeValueLayer =
Chris@187 1411 (haveCurrentLayer &&
Chris@187 1412 qobject_cast<TimeValueLayer *>(currentLayer));
Chris@187 1413 bool pitchCandidatesVisible =
Chris@187 1414 m_analyser->arePitchCandidatesShown();
Chris@0 1415
Chris@0 1416 emit canChangePlaybackSpeed(true);
Chris@0 1417 int v = m_playSpeed->value();
Chris@0 1418 emit canSpeedUpPlayback(v < m_playSpeed->maximum());
Chris@0 1419 emit canSlowDownPlayback(v > m_playSpeed->minimum());
Chris@0 1420
Chris@291 1421 bool haveWaveform =
Chris@291 1422 m_analyser->isVisible(Analyser::Audio) &&
Chris@291 1423 m_analyser->getLayer(Analyser::Audio);
Chris@291 1424
Chris@237 1425 bool havePitchTrack =
Chris@237 1426 m_analyser->isVisible(Analyser::PitchTrack) &&
Chris@237 1427 m_analyser->getLayer(Analyser::PitchTrack);
Chris@237 1428
Chris@237 1429 bool haveNotes =
Chris@237 1430 m_analyser->isVisible(Analyser::Notes) &&
Chris@237 1431 m_analyser->getLayer(Analyser::Notes);
Chris@237 1432
Chris@237 1433 emit canExportPitchTrack(havePitchTrack);
Chris@237 1434 emit canExportNotes(haveNotes);
Chris@237 1435 emit canSnapNotes(haveSelection && haveNotes);
Chris@224 1436
Chris@291 1437 emit canPlayWaveform(haveWaveform && haveMainModel && havePlayTarget);
Chris@291 1438 emit canPlayPitch(havePitchTrack && haveMainModel && havePlayTarget);
Chris@291 1439 emit canPlayNotes(haveNotes && haveMainModel && havePlayTarget);
Chris@291 1440
Chris@187 1441 if (pitchCandidatesVisible) {
Chris@187 1442 m_showCandidatesAction->setText(tr("Hide Pitch Candidates"));
Chris@187 1443 m_showCandidatesAction->setStatusTip(tr("Remove the display of alternate pitch candidates for the selected region"));
Chris@187 1444 } else {
Chris@187 1445 m_showCandidatesAction->setText(tr("Show Pitch Candidates"));
Chris@187 1446 m_showCandidatesAction->setStatusTip(tr("Show alternate pitch candidates for the selected region"));
Chris@187 1447 }
Chris@187 1448
Chris@0 1449 if (m_ffwdAction && m_rwdAction) {
Chris@0 1450 if (haveCurrentTimeInstantsLayer) {
Chris@0 1451 m_ffwdAction->setText(tr("Fast Forward to Next Instant"));
Chris@0 1452 m_ffwdAction->setStatusTip(tr("Fast forward to the next time instant in the current layer"));
Chris@0 1453 m_rwdAction->setText(tr("Rewind to Previous Instant"));
Chris@0 1454 m_rwdAction->setStatusTip(tr("Rewind to the previous time instant in the current layer"));
Chris@0 1455 } else if (haveCurrentTimeValueLayer) {
Chris@0 1456 m_ffwdAction->setText(tr("Fast Forward to Next Point"));
Chris@0 1457 m_ffwdAction->setStatusTip(tr("Fast forward to the next point in the current layer"));
Chris@0 1458 m_rwdAction->setText(tr("Rewind to Previous Point"));
Chris@0 1459 m_rwdAction->setStatusTip(tr("Rewind to the previous point in the current layer"));
Chris@0 1460 } else {
Chris@0 1461 m_ffwdAction->setText(tr("Fast Forward"));
Chris@0 1462 m_ffwdAction->setStatusTip(tr("Fast forward"));
Chris@0 1463 m_rwdAction->setText(tr("Rewind"));
Chris@0 1464 m_rwdAction->setStatusTip(tr("Rewind"));
Chris@0 1465 }
Chris@0 1466 }
Chris@0 1467 }
Chris@0 1468
Chris@0 1469 void
Chris@144 1470 MainWindow::showAudioToggled()
Chris@128 1471 {
Chris@144 1472 m_analyser->toggleVisible(Analyser::Audio);
justin@220 1473
Chris@424 1474 QSettings settings;
Chris@424 1475 settings.beginGroup("MainWindow");
Chris@424 1476
Chris@424 1477 bool playOn = false;
Chris@424 1478 if (m_analyser->isVisible(Analyser::Audio)) {
Chris@424 1479 // just switched layer on; check whether playback was also on previously
Chris@424 1480 playOn = settings.value("playaudiowas", true).toBool();
Chris@424 1481 } else {
Chris@424 1482 settings.setValue("playaudiowas", m_playAudio->isChecked());
Chris@424 1483 }
Chris@291 1484 m_analyser->setAudible(Analyser::Audio, playOn);
Chris@424 1485
Chris@424 1486 settings.endGroup();
Chris@291 1487
Chris@291 1488 updateMenuStates();
Chris@424 1489 updateLayerStatuses();
Chris@128 1490 }
Chris@128 1491
Chris@128 1492 void
Chris@144 1493 MainWindow::showPitchToggled()
Chris@128 1494 {
Chris@144 1495 m_analyser->toggleVisible(Analyser::PitchTrack);
justin@185 1496
Chris@424 1497 QSettings settings;
Chris@424 1498 settings.beginGroup("MainWindow");
Chris@424 1499
Chris@424 1500 bool playOn = false;
Chris@424 1501 if (m_analyser->isVisible(Analyser::PitchTrack)) {
Chris@424 1502 // just switched layer on; check whether playback was also on previously
Chris@424 1503 playOn = settings.value("playpitchwas", true).toBool();
Chris@424 1504 } else {
Chris@424 1505 settings.setValue("playpitchwas", m_playPitch->isChecked());
Chris@424 1506 }
Chris@424 1507 m_analyser->setAudible(Analyser::PitchTrack, playOn);
Chris@424 1508
Chris@424 1509 settings.endGroup();
Chris@291 1510
Chris@291 1511 updateMenuStates();
Chris@424 1512 updateLayerStatuses();
Chris@128 1513 }
Chris@128 1514
Chris@128 1515 void
Chris@145 1516 MainWindow::showSpectToggled()
Chris@145 1517 {
Chris@145 1518 m_analyser->toggleVisible(Analyser::Spectrogram);
Chris@145 1519 }
Chris@145 1520
Chris@145 1521 void
Chris@144 1522 MainWindow::showNotesToggled()
Chris@128 1523 {
Chris@144 1524 m_analyser->toggleVisible(Analyser::Notes);
justin@220 1525
Chris@424 1526 QSettings settings;
Chris@424 1527 settings.beginGroup("MainWindow");
Chris@424 1528
Chris@424 1529 bool playOn = false;
Chris@424 1530 if (m_analyser->isVisible(Analyser::Notes)) {
Chris@424 1531 // just switched layer on; check whether playback was also on previously
Chris@424 1532 playOn = settings.value("playnoteswas", true).toBool();
Chris@424 1533 } else {
Chris@424 1534 settings.setValue("playnoteswas", m_playNotes->isChecked());
Chris@424 1535 }
Chris@424 1536 m_analyser->setAudible(Analyser::Notes, playOn);
Chris@424 1537
Chris@424 1538 settings.endGroup();
Chris@291 1539
Chris@291 1540 updateMenuStates();
Chris@424 1541 updateLayerStatuses();
Chris@144 1542 }
Chris@144 1543
Chris@144 1544 void
Chris@144 1545 MainWindow::playAudioToggled()
Chris@144 1546 {
Chris@144 1547 m_analyser->toggleAudible(Analyser::Audio);
Chris@424 1548 updateLayerStatuses();
Chris@144 1549 }
Chris@144 1550
Chris@144 1551 void
Chris@144 1552 MainWindow::playPitchToggled()
Chris@144 1553 {
Chris@144 1554 m_analyser->toggleAudible(Analyser::PitchTrack);
Chris@424 1555 updateLayerStatuses();
Chris@144 1556 }
Chris@144 1557
Chris@144 1558 void
Chris@144 1559 MainWindow::playNotesToggled()
Chris@144 1560 {
Chris@144 1561 m_analyser->toggleAudible(Analyser::Notes);
Chris@424 1562 updateLayerStatuses();
Chris@144 1563 }
Chris@144 1564
Chris@144 1565 void
Chris@144 1566 MainWindow::updateLayerStatuses()
Chris@144 1567 {
Chris@144 1568 m_showAudio->setChecked(m_analyser->isVisible(Analyser::Audio));
Chris@424 1569 m_playAudio->setChecked(m_analyser->isAudible(Analyser::Audio));
Chris@424 1570 m_audioLPW->setEnabled(m_analyser->isAudible(Analyser::Audio));
Chris@405 1571 m_audioLPW->setLevel(m_analyser->getGain(Analyser::Audio));
Chris@405 1572 m_audioLPW->setPan(m_analyser->getPan(Analyser::Audio));
Chris@424 1573
Chris@405 1574 m_showPitch->setChecked(m_analyser->isVisible(Analyser::PitchTrack));
Chris@424 1575 m_playPitch->setChecked(m_analyser->isAudible(Analyser::PitchTrack));
Chris@424 1576 m_pitchLPW->setEnabled(m_analyser->isAudible(Analyser::PitchTrack));
Chris@405 1577 m_pitchLPW->setLevel(m_analyser->getGain(Analyser::PitchTrack));
Chris@405 1578 m_pitchLPW->setPan(m_analyser->getPan(Analyser::PitchTrack));
Chris@405 1579
Chris@405 1580 m_showNotes->setChecked(m_analyser->isVisible(Analyser::Notes));
Chris@424 1581 m_playNotes->setChecked(m_analyser->isAudible(Analyser::Notes));
Chris@424 1582 m_notesLPW->setEnabled(m_analyser->isAudible(Analyser::Notes));
Chris@405 1583 m_notesLPW->setLevel(m_analyser->getGain(Analyser::Notes));
Chris@405 1584 m_notesLPW->setPan(m_analyser->getPan(Analyser::Notes));
Chris@405 1585
Chris@145 1586 m_showSpect->setChecked(m_analyser->isVisible(Analyser::Spectrogram));
Chris@128 1587 }
Chris@128 1588
Chris@128 1589 void
Chris@227 1590 MainWindow::editDisplayExtents()
Chris@227 1591 {
Chris@399 1592 double min, max;
Chris@399 1593 double vmin = 0;
Chris@399 1594 double vmax = getMainModel()->getSampleRate() /2;
Chris@227 1595
Chris@227 1596 if (!m_analyser->getDisplayFrequencyExtents(min, max)) {
Chris@227 1597 //!!!
Chris@227 1598 return;
Chris@227 1599 }
Chris@227 1600
Chris@227 1601 RangeInputDialog dialog(tr("Set frequency range"),
Chris@227 1602 tr("Enter new frequency range, from %1 to %2 Hz.\nThese values will be rounded to the nearest spectrogram bin.")
Chris@227 1603 .arg(vmin).arg(vmax),
Chris@399 1604 "Hz", float(vmin), float(vmax), this);
Chris@399 1605 dialog.setRange(float(min), float(max));
Chris@227 1606
Chris@227 1607 if (dialog.exec() == QDialog::Accepted) {
Chris@399 1608 float fmin, fmax;
Chris@399 1609 dialog.getRange(fmin, fmax);
Chris@399 1610 min = fmin;
Chris@399 1611 max = fmax;
Chris@227 1612 if (min > max) {
Chris@399 1613 double tmp = max;
Chris@227 1614 max = min;
Chris@227 1615 min = tmp;
Chris@227 1616 }
Chris@227 1617 m_analyser->setDisplayFrequencyExtents(min, max);
Chris@227 1618 }
Chris@227 1619 }
Chris@227 1620
Chris@227 1621 void
Chris@0 1622 MainWindow::updateDescriptionLabel()
Chris@0 1623 {
Chris@6 1624 // Nothing, we don't have one
Chris@0 1625 }
Chris@0 1626
Chris@0 1627 void
Chris@0 1628 MainWindow::documentModified()
Chris@0 1629 {
Chris@0 1630 MainWindowBase::documentModified();
Chris@0 1631 }
Chris@0 1632
Chris@0 1633 void
Chris@0 1634 MainWindow::documentRestored()
Chris@0 1635 {
Chris@0 1636 MainWindowBase::documentRestored();
Chris@0 1637 }
Chris@0 1638
Chris@0 1639 void
Chris@0 1640 MainWindow::newSession()
Chris@0 1641 {
Chris@0 1642 if (!checkSaveModified()) return;
Chris@0 1643
Chris@0 1644 closeSession();
Chris@0 1645 createDocument();
Chris@0 1646 m_document->setAutoAlignment(true);
Chris@0 1647
Chris@586 1648 Pane *pane = m_paneStack->addPane(true);
Chris@362 1649 pane->setPlaybackFollow(PlaybackScrollPage);
Chris@0 1650
Chris@391 1651 m_viewManager->setGlobalCentreFrame
Chris@391 1652 (pane->getFrameForX(width() / 2));
Chris@391 1653
Chris@0 1654 connect(pane, SIGNAL(contextHelpChanged(const QString &)),
Chris@0 1655 this, SLOT(contextHelpChanged(const QString &)));
Chris@0 1656
Chris@6 1657 // Layer *waveform = m_document->createMainModelLayer(LayerFactory::Waveform);
Chris@6 1658 // m_document->addLayerToView(pane, waveform);
Chris@0 1659
Chris@0 1660 m_overview->registerView(pane);
Chris@0 1661
Chris@0 1662 CommandHistory::getInstance()->clear();
Chris@0 1663 CommandHistory::getInstance()->documentSaved();
Chris@0 1664 documentRestored();
Chris@0 1665 updateMenuStates();
Chris@0 1666 }
Chris@0 1667
Chris@0 1668 void
Chris@244 1669 MainWindow::documentReplaced()
Chris@244 1670 {
Chris@244 1671 if (m_document) {
Chris@244 1672 connect(m_document, SIGNAL(activity(QString)),
Chris@244 1673 m_activityLog, SLOT(activityHappened(QString)));
Chris@244 1674 }
Chris@244 1675 }
Chris@244 1676
Chris@244 1677 void
Chris@0 1678 MainWindow::closeSession()
Chris@0 1679 {
Chris@0 1680 if (!checkSaveModified()) return;
Chris@0 1681
Chris@226 1682 m_analyser->fileClosed();
Chris@226 1683
Chris@0 1684 while (m_paneStack->getPaneCount() > 0) {
Chris@0 1685
Chris@167 1686 Pane *pane = m_paneStack->getPane(m_paneStack->getPaneCount() - 1);
Chris@0 1687
Chris@167 1688 while (pane->getLayerCount() > 0) {
Chris@167 1689 m_document->removeLayerFromView
Chris@167 1690 (pane, pane->getLayer(pane->getLayerCount() - 1));
Chris@167 1691 }
Chris@167 1692
Chris@167 1693 m_overview->unregisterView(pane);
Chris@167 1694 m_paneStack->deletePane(pane);
Chris@0 1695 }
Chris@0 1696
Chris@0 1697 while (m_paneStack->getHiddenPaneCount() > 0) {
Chris@0 1698
Chris@167 1699 Pane *pane = m_paneStack->getHiddenPane
Chris@167 1700 (m_paneStack->getHiddenPaneCount() - 1);
Chris@167 1701
Chris@167 1702 while (pane->getLayerCount() > 0) {
Chris@167 1703 m_document->removeLayerFromView
Chris@167 1704 (pane, pane->getLayer(pane->getLayerCount() - 1));
Chris@167 1705 }
Chris@167 1706
Chris@167 1707 m_overview->unregisterView(pane);
Chris@167 1708 m_paneStack->deletePane(pane);
Chris@0 1709 }
Chris@0 1710
Chris@0 1711 delete m_document;
Chris@0 1712 m_document = 0;
Chris@0 1713 m_viewManager->clearSelections();
Chris@0 1714 m_timeRulerLayer = 0; // document owned this
Chris@0 1715
Chris@0 1716 m_sessionFile = "";
Chris@0 1717
Chris@0 1718 CommandHistory::getInstance()->clear();
Chris@0 1719 CommandHistory::getInstance()->documentSaved();
Chris@0 1720 documentRestored();
Chris@0 1721 }
Chris@0 1722
Chris@0 1723 void
Chris@0 1724 MainWindow::openFile()
Chris@0 1725 {
Chris@0 1726 QString orig = m_audioFile;
Chris@0 1727 if (orig == "") orig = ".";
Chris@0 1728 else orig = QFileInfo(orig).absoluteDir().canonicalPath();
Chris@0 1729
Chris@0 1730 QString path = getOpenFileName(FileFinder::AnyFile);
Chris@0 1731
Chris@0 1732 if (path.isEmpty()) return;
Chris@0 1733
Chris@387 1734 FileOpenStatus status = openPath(path, ReplaceSession);
Chris@0 1735
Chris@0 1736 if (status == FileOpenFailed) {
Chris@0 1737 QMessageBox::critical(this, tr("Failed to open file"),
Chris@0 1738 tr("<b>File open failed</b><p>File \"%1\" could not be opened").arg(path));
Chris@0 1739 } else if (status == FileOpenWrongMode) {
Chris@0 1740 QMessageBox::critical(this, tr("Failed to open file"),
Chris@0 1741 tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
Chris@0 1742 }
Chris@0 1743 }
Chris@0 1744
Chris@0 1745 void
Chris@0 1746 MainWindow::openLocation()
Chris@0 1747 {
Chris@0 1748 QSettings settings;
Chris@0 1749 settings.beginGroup("MainWindow");
Chris@0 1750 QString lastLocation = settings.value("lastremote", "").toString();
Chris@0 1751
Chris@0 1752 bool ok = false;
Chris@0 1753 QString text = QInputDialog::getText
Chris@0 1754 (this, tr("Open Location"),
Chris@0 1755 tr("Please enter the URL of the location to open:"),
Chris@0 1756 QLineEdit::Normal, lastLocation, &ok);
Chris@0 1757
Chris@0 1758 if (!ok) return;
Chris@0 1759
Chris@0 1760 settings.setValue("lastremote", text);
Chris@0 1761
Chris@0 1762 if (text.isEmpty()) return;
Chris@0 1763
Chris@387 1764 FileOpenStatus status = openPath(text, ReplaceSession);
Chris@0 1765
Chris@0 1766 if (status == FileOpenFailed) {
Chris@0 1767 QMessageBox::critical(this, tr("Failed to open location"),
Chris@0 1768 tr("<b>Open failed</b><p>URL \"%1\" could not be opened").arg(text));
Chris@0 1769 } else if (status == FileOpenWrongMode) {
Chris@0 1770 QMessageBox::critical(this, tr("Failed to open location"),
Chris@0 1771 tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
Chris@0 1772 }
Chris@0 1773 }
Chris@0 1774
Chris@0 1775 void
Chris@0 1776 MainWindow::openRecentFile()
Chris@0 1777 {
Chris@0 1778 QObject *obj = sender();
Chris@3 1779 QAction *action = qobject_cast<QAction *>(obj);
Chris@0 1780
Chris@0 1781 if (!action) {
Chris@188 1782 cerr << "WARNING: MainWindow::openRecentFile: sender is not an action"
Chris@188 1783 << endl;
Chris@188 1784 return;
Chris@0 1785 }
Chris@0 1786
Chris@0 1787 QString path = action->text();
Chris@0 1788 if (path == "") return;
Chris@0 1789
Chris@387 1790 FileOpenStatus status = openPath(path, ReplaceSession);
Chris@0 1791
Chris@0 1792 if (status == FileOpenFailed) {
Chris@0 1793 QMessageBox::critical(this, tr("Failed to open location"),
Chris@0 1794 tr("<b>Open failed</b><p>File or URL \"%1\" could not be opened").arg(path));
Chris@0 1795 } else if (status == FileOpenWrongMode) {
Chris@0 1796 QMessageBox::critical(this, tr("Failed to open location"),
Chris@0 1797 tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
Chris@0 1798 }
Chris@0 1799 }
Chris@0 1800
Chris@0 1801 void
Chris@0 1802 MainWindow::paneAdded(Pane *pane)
Chris@0 1803 {
Chris@6 1804 pane->setPlaybackFollow(PlaybackScrollPage);
Chris@0 1805 m_paneStack->sizePanesEqually();
Chris@0 1806 if (m_overview) m_overview->registerView(pane);
Chris@0 1807 }
Chris@0 1808
Chris@0 1809 void
Chris@0 1810 MainWindow::paneHidden(Pane *pane)
Chris@0 1811 {
Chris@0 1812 if (m_overview) m_overview->unregisterView(pane);
Chris@0 1813 }
Chris@0 1814
Chris@0 1815 void
Chris@0 1816 MainWindow::paneAboutToBeDeleted(Pane *pane)
Chris@0 1817 {
Chris@0 1818 if (m_overview) m_overview->unregisterView(pane);
Chris@0 1819 }
Chris@0 1820
Chris@0 1821 void
Chris@0 1822 MainWindow::paneDropAccepted(Pane *pane, QStringList uriList)
Chris@0 1823 {
Chris@4 1824 if (pane) m_paneStack->setCurrentPane(pane);
Chris@0 1825
Chris@0 1826 for (QStringList::iterator i = uriList.begin(); i != uriList.end(); ++i) {
Chris@0 1827
Chris@387 1828 FileOpenStatus status = openPath(*i, ReplaceSession);
Chris@0 1829
Chris@0 1830 if (status == FileOpenFailed) {
Chris@0 1831 QMessageBox::critical(this, tr("Failed to open dropped URL"),
Chris@0 1832 tr("<b>Open failed</b><p>Dropped URL \"%1\" could not be opened").arg(*i));
Chris@0 1833 } else if (status == FileOpenWrongMode) {
Chris@0 1834 QMessageBox::critical(this, tr("Failed to open dropped URL"),
Chris@0 1835 tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
Chris@0 1836 }
Chris@0 1837 }
Chris@0 1838 }
Chris@0 1839
Chris@0 1840 void
Chris@0 1841 MainWindow::paneDropAccepted(Pane *pane, QString text)
Chris@0 1842 {
Chris@0 1843 if (pane) m_paneStack->setCurrentPane(pane);
Chris@0 1844
Chris@0 1845 QUrl testUrl(text);
Chris@0 1846 if (testUrl.scheme() == "file" ||
Chris@0 1847 testUrl.scheme() == "http" ||
Chris@0 1848 testUrl.scheme() == "ftp") {
Chris@0 1849 QStringList list;
Chris@0 1850 list.push_back(text);
Chris@0 1851 paneDropAccepted(pane, list);
Chris@0 1852 return;
Chris@0 1853 }
Chris@0 1854
Chris@0 1855 //!!! open as text -- but by importing as if a CSV, or just adding
Chris@0 1856 //to a text layer?
Chris@0 1857 }
Chris@0 1858
Chris@0 1859 void
Chris@0 1860 MainWindow::closeEvent(QCloseEvent *e)
Chris@0 1861 {
Chris@70 1862 // cerr << "MainWindow::closeEvent" << endl;
Chris@0 1863
Chris@0 1864 if (m_openingAudioFile) {
Chris@70 1865 // cerr << "Busy - ignoring close event" << endl;
Chris@257 1866 e->ignore();
Chris@257 1867 return;
Chris@0 1868 }
Chris@0 1869
Chris@0 1870 if (!m_abandoning && !checkSaveModified()) {
Chris@70 1871 // cerr << "Ignoring close event" << endl;
Chris@257 1872 e->ignore();
Chris@257 1873 return;
Chris@0 1874 }
Chris@0 1875
Chris@0 1876 QSettings settings;
Chris@0 1877 settings.beginGroup("MainWindow");
Chris@0 1878 settings.setValue("size", size());
Chris@0 1879 settings.setValue("position", pos());
Chris@0 1880 settings.endGroup();
Chris@0 1881
Chris@0 1882 delete m_keyReference;
Chris@0 1883 m_keyReference = 0;
Chris@0 1884
Chris@0 1885 closeSession();
Chris@0 1886
Chris@0 1887 e->accept();
Chris@0 1888 return;
Chris@0 1889 }
Chris@0 1890
Chris@0 1891 bool
Chris@0 1892 MainWindow::commitData(bool mayAskUser)
Chris@0 1893 {
Chris@0 1894 if (mayAskUser) {
Chris@0 1895 bool rv = checkSaveModified();
Chris@0 1896 return rv;
Chris@0 1897 } else {
Chris@0 1898 if (!m_documentModified) return true;
Chris@0 1899
Chris@0 1900 // If we can't check with the user first, then we can't save
Chris@0 1901 // to the original session file (even if we have it) -- have
Chris@0 1902 // to use a temporary file
Chris@0 1903
Chris@0 1904 QString svDirBase = ".sv1";
Chris@0 1905 QString svDir = QDir::home().filePath(svDirBase);
Chris@0 1906
Chris@0 1907 if (!QFileInfo(svDir).exists()) {
Chris@0 1908 if (!QDir::home().mkdir(svDirBase)) return false;
Chris@0 1909 } else {
Chris@0 1910 if (!QFileInfo(svDir).isDir()) return false;
Chris@0 1911 }
Chris@0 1912
Chris@0 1913 // This name doesn't have to be unguessable
Chris@0 1914 #ifndef _WIN32
Chris@0 1915 QString fname = QString("tmp-%1-%2.sv")
Chris@0 1916 .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz"))
Chris@0 1917 .arg(QProcess().pid());
Chris@0 1918 #else
Chris@0 1919 QString fname = QString("tmp-%1.sv")
Chris@0 1920 .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz"));
Chris@0 1921 #endif
Chris@0 1922 QString fpath = QDir(svDir).filePath(fname);
Chris@0 1923 if (saveSessionFile(fpath)) {
Chris@0 1924 m_recentFiles.addFile(fpath);
Chris@0 1925 return true;
Chris@0 1926 } else {
Chris@0 1927 return false;
Chris@0 1928 }
Chris@0 1929 }
Chris@0 1930 }
Chris@0 1931
Chris@0 1932 bool
Chris@0 1933 MainWindow::checkSaveModified()
Chris@0 1934 {
Chris@0 1935 // Called before some destructive operation (e.g. new session,
Chris@0 1936 // exit program). Return true if we can safely proceed, false to
Chris@0 1937 // cancel.
Chris@0 1938
Chris@0 1939 if (!m_documentModified) return true;
Chris@0 1940
Chris@0 1941 int button =
Chris@257 1942 QMessageBox::warning(this,
Chris@257 1943 tr("Session modified"),
Chris@257 1944 tr("The current session has been modified.\nDo you want to save it?"),
Chris@257 1945 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@0 1946 QMessageBox::Yes);
Chris@0 1947
Chris@0 1948 if (button == QMessageBox::Yes) {
Chris@257 1949 saveSession();
Chris@257 1950 if (m_documentModified) { // save failed -- don't proceed!
Chris@257 1951 return false;
Chris@257 1952 } else {
Chris@0 1953 return true; // saved, so it's safe to continue now
Chris@0 1954 }
Chris@0 1955 } else if (button == QMessageBox::No) {
Chris@257 1956 m_documentModified = false; // so we know to abandon it
Chris@257 1957 return true;
Chris@0 1958 }
Chris@0 1959
Chris@0 1960 // else cancel
Chris@0 1961 return false;
Chris@0 1962 }
Chris@0 1963
Chris@314 1964 bool
Chris@314 1965 MainWindow::waitForInitialAnalysis()
Chris@314 1966 {
Chris@314 1967 // Called before saving a session. We can't safely save while the
Chris@314 1968 // initial analysis is happening, because then we end up with an
Chris@314 1969 // incomplete session on reload. There are certainly theoretically
Chris@314 1970 // better ways to handle this...
Chris@314 1971
Chris@330 1972 QSettings settings;
Chris@330 1973 settings.beginGroup("Analyser");
Chris@330 1974 bool autoAnalyse = settings.value("auto-analysis", true).toBool();
Chris@330 1975 settings.endGroup();
Chris@330 1976
Chris@330 1977 if (!autoAnalyse) {
Chris@330 1978 return true;
Chris@330 1979 }
Chris@330 1980
Chris@314 1981 if (!m_analyser || m_analyser->getInitialAnalysisCompletion() >= 100) {
Chris@314 1982 return true;
Chris@314 1983 }
Chris@314 1984
Chris@314 1985 QMessageBox mb(QMessageBox::Information,
Chris@314 1986 tr("Waiting for analysis"),
Chris@442 1987 tr("Waiting for initial analysis to finish before loading or saving..."),
Chris@314 1988 QMessageBox::Cancel,
Chris@314 1989 this);
Chris@314 1990
Chris@314 1991 connect(m_analyser, SIGNAL(initialAnalysisCompleted()),
Chris@314 1992 &mb, SLOT(accept()));
Chris@314 1993
Chris@314 1994 if (mb.exec() == QDialog::Accepted) {
Chris@314 1995 return true;
Chris@314 1996 } else {
Chris@314 1997 return false;
Chris@314 1998 }
Chris@314 1999 }
Chris@314 2000
Chris@0 2001 void
Chris@0 2002 MainWindow::saveSession()
Chris@0 2003 {
Chris@269 2004 // We do not want to save mid-analysis regions -- that would cause
Chris@269 2005 // confusion on reloading
Chris@269 2006 m_analyser->clearReAnalysis();
Chris@269 2007 clearSelection();
Chris@269 2008
Chris@0 2009 if (m_sessionFile != "") {
Chris@257 2010 if (!saveSessionFile(m_sessionFile)) {
Chris@257 2011 QMessageBox::critical
Chris@257 2012 (this, tr("Failed to save file"),
Chris@257 2013 tr("Session file \"%1\" could not be saved.").arg(m_sessionFile));
Chris@257 2014 } else {
Chris@257 2015 CommandHistory::getInstance()->documentSaved();
Chris@257 2016 documentRestored();
Chris@257 2017 }
Chris@0 2018 } else {
Chris@257 2019 saveSessionAs();
Chris@0 2020 }
Chris@0 2021 }
Chris@0 2022
Chris@0 2023 void
matthiasm@310 2024 MainWindow::saveSessionInAudioPath()
matthiasm@310 2025 {
matthiasm@311 2026 if (m_audioFile == "") return;
matthiasm@311 2027
Chris@314 2028 if (!waitForInitialAnalysis()) return;
Chris@314 2029
matthiasm@310 2030 // We do not want to save mid-analysis regions -- that would cause
matthiasm@310 2031 // confusion on reloading
matthiasm@310 2032 m_analyser->clearReAnalysis();
matthiasm@310 2033 clearSelection();
matthiasm@310 2034
matthiasm@310 2035 QString filepath = QFileInfo(m_audioFile).absoluteDir().canonicalPath();
matthiasm@310 2036 QString basename = QFileInfo(m_audioFile).completeBaseName();
matthiasm@310 2037
matthiasm@310 2038 QString path = QDir(filepath).filePath(basename + ".ton");
matthiasm@310 2039
matthiasm@310 2040 cerr << path << endl;
matthiasm@310 2041
Chris@313 2042 // We don't want to overwrite an existing .ton file unless we put
Chris@313 2043 // it there in the first place
Chris@313 2044 bool shouldVerify = true;
Chris@313 2045 if (m_sessionFile == path) {
Chris@313 2046 shouldVerify = false;
Chris@313 2047 }
Chris@313 2048
Chris@313 2049 if (shouldVerify && QFileInfo(path).exists()) {
Chris@313 2050 if (QMessageBox::question(0, tr("File exists"),
Chris@313 2051 tr("<b>File exists</b><p>The file \"%1\" already exists.\nDo you want to overwrite it?").arg(path),
Chris@313 2052 QMessageBox::Ok,
Chris@313 2053 QMessageBox::Cancel) != QMessageBox::Ok) {
Chris@313 2054 return;
Chris@313 2055 }
Chris@313 2056 }
Chris@313 2057
Chris@314 2058 if (!waitForInitialAnalysis()) {
Chris@314 2059 QMessageBox::warning(this, tr("File not saved"),
Chris@314 2060 tr("Wait cancelled: the session has not been saved."));
Chris@314 2061 }
Chris@314 2062
matthiasm@310 2063 if (!saveSessionFile(path)) {
matthiasm@310 2064 QMessageBox::critical(this, tr("Failed to save file"),
matthiasm@310 2065 tr("Session file \"%1\" could not be saved.").arg(path));
matthiasm@310 2066 } else {
matthiasm@310 2067 setWindowTitle(tr("%1: %2")
matthiasm@310 2068 .arg(QApplication::applicationName())
matthiasm@310 2069 .arg(QFileInfo(path).fileName()));
matthiasm@310 2070 m_sessionFile = path;
matthiasm@310 2071 CommandHistory::getInstance()->documentSaved();
matthiasm@310 2072 documentRestored();
matthiasm@310 2073 m_recentFiles.addFile(path);
matthiasm@310 2074 }
matthiasm@310 2075 }
matthiasm@310 2076
matthiasm@310 2077 void
Chris@0 2078 MainWindow::saveSessionAs()
Chris@0 2079 {
Chris@270 2080 // We do not want to save mid-analysis regions -- that would cause
Chris@270 2081 // confusion on reloading
Chris@270 2082 m_analyser->clearReAnalysis();
Chris@270 2083 clearSelection();
Chris@270 2084
Chris@0 2085 QString path = getSaveFileName(FileFinder::SessionFile);
Chris@0 2086
Chris@309 2087 if (path == "") {
Chris@309 2088 return;
Chris@309 2089 }
Chris@0 2090
Chris@314 2091 if (!waitForInitialAnalysis()) {
Chris@314 2092 QMessageBox::warning(this, tr("File not saved"),
Chris@314 2093 tr("Wait cancelled: the session has not been saved."));
Chris@314 2094 return;
Chris@314 2095 }
Chris@314 2096
Chris@0 2097 if (!saveSessionFile(path)) {
Chris@85 2098 QMessageBox::critical(this, tr("Failed to save file"),
Chris@85 2099 tr("Session file \"%1\" could not be saved.").arg(path));
Chris@0 2100 } else {
Chris@85 2101 setWindowTitle(tr("%1: %2")
Chris@0 2102 .arg(QApplication::applicationName())
Chris@85 2103 .arg(QFileInfo(path).fileName()));
Chris@85 2104 m_sessionFile = path;
Chris@85 2105 CommandHistory::getInstance()->documentSaved();
Chris@85 2106 documentRestored();
Chris@0 2107 m_recentFiles.addFile(path);
Chris@0 2108 }
Chris@0 2109 }
Chris@0 2110
Chris@85 2111 QString
Chris@85 2112 MainWindow::exportToSVL(QString path, Layer *layer)
Chris@85 2113 {
Chris@573 2114 auto model = ModelById::get(layer->getModel());
Chris@573 2115 if (!model) return "Internal error: No model in layer";
Chris@85 2116
Chris@85 2117 QFile file(path);
Chris@85 2118 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
Chris@85 2119 return tr("Failed to open file %1 for writing").arg(path);
Chris@85 2120 } else {
Chris@85 2121 QTextStream out(&file);
Chris@85 2122 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
Chris@85 2123 << "<!DOCTYPE sonic-visualiser>\n"
Chris@85 2124 << "<sv>\n"
Chris@85 2125 << " <data>\n";
Chris@85 2126
Chris@85 2127 model->toXml(out, " ");
Chris@85 2128
Chris@85 2129 out << " </data>\n"
Chris@85 2130 << " <display>\n";
Chris@85 2131
Chris@85 2132 layer->toXml(out, " ");
Chris@85 2133
Chris@85 2134 out << " </display>\n"
Chris@85 2135 << "</sv>\n";
Chris@85 2136
Chris@85 2137 return "";
Chris@85 2138 }
Chris@85 2139 }
Chris@85 2140
Chris@0 2141 void
Chris@174 2142 MainWindow::importPitchLayer()
Chris@174 2143 {
Chris@174 2144 QString path = getOpenFileName(FileFinder::LayerFileNoMidiNonSV);
Chris@174 2145 if (path == "") return;
Chris@174 2146
Chris@174 2147 FileOpenStatus status = importPitchLayer(path);
Chris@174 2148
Chris@174 2149 if (status == FileOpenFailed) {
Chris@174 2150 emit hideSplash();
Chris@174 2151 QMessageBox::critical(this, tr("Failed to open file"),
Chris@174 2152 tr("<b>File open failed</b><p>Layer file %1 could not be opened.").arg(path));
Chris@174 2153 return;
Chris@174 2154 } else if (status == FileOpenWrongMode) {
Chris@174 2155 emit hideSplash();
Chris@174 2156 QMessageBox::critical(this, tr("Failed to open file"),
Chris@174 2157 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 2158 }
Chris@174 2159 }
Chris@174 2160
Chris@174 2161 MainWindow::FileOpenStatus
Chris@174 2162 MainWindow::importPitchLayer(FileSource source)
Chris@174 2163 {
Chris@174 2164 if (!source.isAvailable()) return FileOpenFailed;
Chris@174 2165 source.waitForData();
Chris@174 2166
Chris@442 2167 if (!waitForInitialAnalysis()) return FileOpenCancelled;
Chris@442 2168
Chris@174 2169 QString path = source.getLocalFilename();
Chris@174 2170
Chris@174 2171 RDFImporter::RDFDocumentType rdfType =
Chris@174 2172 RDFImporter::identifyDocumentType(QUrl::fromLocalFile(path).toString());
Chris@174 2173
Chris@174 2174 if (rdfType != RDFImporter::NotRDF) {
Chris@174 2175
Chris@174 2176 //!!!
Chris@174 2177 return FileOpenFailed;
Chris@174 2178
Chris@174 2179 } else if (source.getExtension().toLower() == "svl" ||
Chris@174 2180 (source.getExtension().toLower() == "xml" &&
Chris@174 2181 (SVFileReader::identifyXmlFile(source.getLocalFilename())
Chris@174 2182 == SVFileReader::SVLayerFile))) {
Chris@174 2183
Chris@174 2184 //!!!
Chris@174 2185 return FileOpenFailed;
Chris@174 2186
Chris@174 2187 } else {
Chris@174 2188
Chris@174 2189 try {
Chris@174 2190
Chris@174 2191 CSVFormat format(path);
Chris@174 2192 format.setSampleRate(getMainModel()->getSampleRate());
Chris@174 2193
Chris@174 2194 if (format.getModelType() != CSVFormat::TwoDimensionalModel) {
Chris@174 2195 //!!! error report
Chris@174 2196 return FileOpenFailed;
Chris@174 2197 }
Chris@174 2198
Chris@174 2199 Model *model = DataFileReaderFactory::loadCSV
Chris@174 2200 (path, format, getMainModel()->getSampleRate());
Chris@174 2201
Chris@174 2202 if (model) {
Chris@174 2203
Chris@174 2204 SVDEBUG << "MainWindow::importPitchLayer: Have model" << endl;
Chris@174 2205
Chris@573 2206 ModelId modelId = ModelById::add
Chris@573 2207 (std::shared_ptr<Model>(model));
Chris@573 2208
Chris@174 2209 CommandHistory::getInstance()->startCompoundOperation
Chris@174 2210 (tr("Import Pitch Track"), true);
Chris@174 2211
Chris@573 2212 Layer *newLayer = m_document->createImportedLayer(modelId);
Chris@174 2213
Chris@174 2214 m_analyser->takePitchTrackFrom(newLayer);
Chris@174 2215
Chris@174 2216 m_document->deleteLayer(newLayer);
Chris@174 2217
Chris@174 2218 CommandHistory::getInstance()->endCompoundOperation();
Chris@174 2219
Chris@174 2220 if (!source.isRemote()) {
Chris@174 2221 registerLastOpenedFilePath
Chris@174 2222 (FileFinder::LayerFile,
Chris@174 2223 path); // for file dialog
Chris@174 2224 }
Chris@174 2225
Chris@174 2226 return FileOpenSucceeded;
Chris@174 2227 }
Chris@174 2228 } catch (DataFileReaderFactory::Exception e) {
Chris@174 2229 if (e == DataFileReaderFactory::ImportCancelled) {
Chris@174 2230 return FileOpenCancelled;
Chris@174 2231 }
Chris@174 2232 }
Chris@174 2233 }
Chris@174 2234
Chris@174 2235 return FileOpenFailed;
Chris@174 2236 }
Chris@174 2237
Chris@174 2238 void
Chris@85 2239 MainWindow::exportPitchLayer()
matthiasm@26 2240 {
Chris@174 2241 Layer *layer = m_analyser->getLayer(Analyser::PitchTrack);
matthiasm@26 2242 if (!layer) return;
matthiasm@26 2243
Chris@573 2244 auto model = ModelById::getAs<SparseTimeValueModel>(layer->getModel());
matthiasm@26 2245 if (!model) return;
matthiasm@26 2246
Chris@96 2247 FileFinder::FileType type = FileFinder::LayerFileNoMidiNonSV;
matthiasm@26 2248
matthiasm@26 2249 QString path = getSaveFileName(type);
matthiasm@26 2250
matthiasm@26 2251 if (path == "") return;
matthiasm@26 2252
Chris@442 2253 if (!waitForInitialAnalysis()) return;
Chris@442 2254
matthiasm@26 2255 if (QFileInfo(path).suffix() == "") path += ".svl";
matthiasm@26 2256
matthiasm@26 2257 QString suffix = QFileInfo(path).suffix().toLower();
matthiasm@26 2258
matthiasm@26 2259 QString error;
matthiasm@26 2260
matthiasm@26 2261 if (suffix == "xml" || suffix == "svl") {
matthiasm@26 2262
Chris@85 2263 error = exportToSVL(path, layer);
matthiasm@26 2264
matthiasm@26 2265 } else if (suffix == "ttl" || suffix == "n3") {
matthiasm@26 2266
Chris@573 2267 RDFExporter exporter(path, model.get());
Chris@85 2268 exporter.write();
Chris@85 2269 if (!exporter.isOK()) {
Chris@85 2270 error = exporter.getError();
Chris@85 2271 }
Chris@85 2272
Chris@85 2273 } else {
Chris@85 2274
Chris@429 2275 DataExportOptions options = DataExportFillGaps;
Chris@429 2276
Chris@573 2277 CSVFileWriter writer(path, model.get(),
Chris@429 2278 ((suffix == "csv") ? "," : "\t"),
Chris@429 2279 options);
Chris@85 2280 writer.write();
Chris@85 2281
Chris@85 2282 if (!writer.isOK()) {
Chris@85 2283 error = writer.getError();
Chris@85 2284 }
Chris@85 2285 }
Chris@85 2286
Chris@85 2287 if (error != "") {
Chris@85 2288 QMessageBox::critical(this, tr("Failed to write file"), error);
Chris@85 2289 } else {
Chris@85 2290 emit activity(tr("Export layer to \"%1\"").arg(path));
Chris@85 2291 }
Chris@85 2292 }
Chris@85 2293
Chris@85 2294 void
Chris@85 2295 MainWindow::exportNoteLayer()
Chris@85 2296 {
Chris@174 2297 Layer *layer = m_analyser->getLayer(Analyser::Notes);
Chris@85 2298 if (!layer) return;
Chris@85 2299
Chris@573 2300 auto model = ModelById::getAs<NoteModel>(layer->getModel());
Chris@85 2301 if (!model) return;
Chris@85 2302
Chris@96 2303 FileFinder::FileType type = FileFinder::LayerFileNonSV;
Chris@85 2304
Chris@85 2305 QString path = getSaveFileName(type);
Chris@85 2306
Chris@85 2307 if (path == "") return;
Chris@85 2308
Chris@85 2309 if (QFileInfo(path).suffix() == "") path += ".svl";
Chris@85 2310
Chris@85 2311 QString suffix = QFileInfo(path).suffix().toLower();
Chris@85 2312
Chris@85 2313 QString error;
Chris@85 2314
Chris@85 2315 if (suffix == "xml" || suffix == "svl") {
Chris@85 2316
Chris@85 2317 error = exportToSVL(path, layer);
Chris@85 2318
Chris@85 2319 } else if (suffix == "mid" || suffix == "midi") {
Chris@85 2320
Chris@573 2321 MIDIFileWriter writer(path, model.get(), model->getSampleRate());
Chris@85 2322 writer.write();
Chris@85 2323 if (!writer.isOK()) {
Chris@85 2324 error = writer.getError();
Chris@85 2325 }
Chris@85 2326
Chris@85 2327 } else if (suffix == "ttl" || suffix == "n3") {
Chris@85 2328
Chris@573 2329 RDFExporter exporter(path, model.get());
Chris@85 2330 exporter.write();
Chris@85 2331 if (!exporter.isOK()) {
Chris@85 2332 error = exporter.getError();
matthiasm@26 2333 }
matthiasm@26 2334
matthiasm@26 2335 } else {
matthiasm@26 2336
Chris@429 2337 DataExportOptions options = DataExportOmitLevels;
Chris@429 2338
Chris@573 2339 CSVFileWriter writer(path, model.get(),
Chris@429 2340 ((suffix == "csv") ? "," : "\t"),
Chris@429 2341 options);
matthiasm@26 2342 writer.write();
matthiasm@26 2343
matthiasm@26 2344 if (!writer.isOK()) {
matthiasm@26 2345 error = writer.getError();
matthiasm@26 2346 }
matthiasm@26 2347 }
matthiasm@26 2348
matthiasm@26 2349 if (error != "") {
matthiasm@26 2350 QMessageBox::critical(this, tr("Failed to write file"), error);
matthiasm@26 2351 } else {
matthiasm@26 2352 emit activity(tr("Export layer to \"%1\"").arg(path));
matthiasm@26 2353 }
matthiasm@26 2354 }
matthiasm@26 2355
Chris@139 2356 void
Chris@485 2357 MainWindow::browseRecordedAudio()
Chris@485 2358 {
Chris@485 2359 if (!m_recordTarget) return;
Chris@485 2360
Chris@539 2361 QString path = RecordDirectory::getRecordContainerDirectory();
Chris@539 2362 if (path == "") path = RecordDirectory::getRecordDirectory();
Chris@485 2363 if (path == "") return;
Chris@485 2364
Chris@485 2365 openLocalFolder(path);
Chris@485 2366 }
Chris@485 2367
Chris@485 2368 void
Chris@399 2369 MainWindow::doubleClickSelectInvoked(sv_frame_t frame)
Chris@139 2370 {
Chris@399 2371 sv_frame_t f0, f1;
Chris@139 2372 m_analyser->getEnclosingSelectionScope(frame, f0, f1);
Chris@139 2373
Chris@139 2374 cerr << "MainWindow::doubleClickSelectInvoked(" << frame << "): [" << f0 << "," << f1 << "]" << endl;
Chris@139 2375
Chris@164 2376 Selection sel(f0, f1);
Chris@164 2377 m_viewManager->setSelection(sel);
Chris@164 2378 }
Chris@164 2379
Chris@164 2380 void
Chris@194 2381 MainWindow::abandonSelection()
Chris@167 2382 {
Chris@194 2383 // Named abandonSelection rather than clearSelection to indicate
Chris@194 2384 // that this is an active operation -- it restores the original
Chris@194 2385 // content of the pitch track in the selected region rather than
Chris@194 2386 // simply un-selecting.
Chris@194 2387
Chris@194 2388 cerr << "MainWindow::abandonSelection()" << endl;
Chris@167 2389
Chris@199 2390 CommandHistory::getInstance()->startCompoundOperation(tr("Abandon Selection"), true);
Chris@168 2391
Chris@194 2392 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@194 2393 if (!selections.empty()) {
Chris@194 2394 Selection sel = *selections.begin();
Chris@199 2395 m_analyser->abandonReAnalysis(sel);
Chris@252 2396 auxSnapNotes(sel);
Chris@194 2397 }
Chris@194 2398
Chris@167 2399 MainWindowBase::clearSelection();
Chris@168 2400
Chris@168 2401 CommandHistory::getInstance()->endCompoundOperation();
Chris@167 2402 }
Chris@167 2403
Chris@167 2404 void
Chris@192 2405 MainWindow::selectionChangedByUser()
Chris@164 2406 {
Chris@221 2407 if (!m_document) {
Chris@221 2408 // we're exiting, most likely
Chris@221 2409 return;
Chris@221 2410 }
Chris@221 2411
Chris@164 2412 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@164 2413
Chris@192 2414 cerr << "MainWindow::selectionChangedByUser" << endl;
Chris@192 2415
Chris@192 2416 m_analyser->showPitchCandidates(m_pendingConstraint.isConstrained());
Chris@190 2417
Chris@164 2418 if (!selections.empty()) {
Chris@164 2419 Selection sel = *selections.begin();
Chris@192 2420 cerr << "MainWindow::selectionChangedByUser: have selection" << endl;
Chris@192 2421 QString error = m_analyser->reAnalyseSelection
Chris@192 2422 (sel, m_pendingConstraint);
Chris@164 2423 if (error != "") {
Chris@164 2424 QMessageBox::critical
Chris@164 2425 (this, tr("Failed to analyse selection"),
Chris@164 2426 tr("<b>Analysis failed</b><p>%2</p>").arg(error));
Chris@164 2427 }
Chris@164 2428 }
Chris@192 2429
Chris@192 2430 m_pendingConstraint = Analyser::FrequencyRange();
Chris@192 2431 }
Chris@192 2432
Chris@192 2433 void
Chris@192 2434 MainWindow::regionOutlined(QRect r)
Chris@192 2435 {
Chris@192 2436 cerr << "MainWindow::regionOutlined(" << r.x() << "," << r.y() << "," << r.width() << "," << r.height() << ")" << endl;
Chris@192 2437
Chris@192 2438 Pane *pane = qobject_cast<Pane *>(sender());
Chris@192 2439 if (!pane) {
Chris@192 2440 cerr << "MainWindow::regionOutlined: not sent by pane, ignoring" << endl;
Chris@192 2441 return;
Chris@192 2442 }
Chris@192 2443
Chris@192 2444 if (!m_analyser) {
Chris@192 2445 cerr << "MainWindow::regionOutlined: no analyser, ignoring" << endl;
Chris@192 2446 return;
Chris@192 2447 }
Chris@192 2448
Chris@192 2449 SpectrogramLayer *spectrogram = qobject_cast<SpectrogramLayer *>
Chris@192 2450 (m_analyser->getLayer(Analyser::Spectrogram));
Chris@192 2451 if (!spectrogram) {
Chris@192 2452 cerr << "MainWindow::regionOutlined: no spectrogram layer, ignoring" << endl;
Chris@192 2453 return;
Chris@192 2454 }
Chris@192 2455
Chris@399 2456 sv_frame_t f0 = pane->getFrameForX(r.x());
Chris@399 2457 sv_frame_t f1 = pane->getFrameForX(r.x() + r.width());
Chris@192 2458
Chris@399 2459 double v0 = spectrogram->getFrequencyForY(pane, r.y() + r.height());
Chris@399 2460 double v1 = spectrogram->getFrequencyForY(pane, r.y());
Chris@192 2461
Chris@192 2462 cerr << "MainWindow::regionOutlined: frame " << f0 << " -> " << f1
Chris@192 2463 << ", frequency " << v0 << " -> " << v1 << endl;
Chris@192 2464
Chris@192 2465 m_pendingConstraint = Analyser::FrequencyRange(v0, v1);
Chris@192 2466
Chris@192 2467 Selection sel(f0, f1);
Chris@192 2468 m_viewManager->setSelection(sel);
Chris@0 2469 }
Chris@0 2470
Chris@0 2471 void
Chris@168 2472 MainWindow::clearPitches()
Chris@168 2473 {
Chris@168 2474 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@168 2475
Chris@168 2476 CommandHistory::getInstance()->startCompoundOperation(tr("Clear Pitches"), true);
Chris@168 2477
Chris@168 2478 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@168 2479 k != selections.end(); ++k) {
Chris@184 2480 m_analyser->deletePitches(*k);
Chris@252 2481 auxSnapNotes(*k);
Chris@168 2482 }
Chris@168 2483
Chris@168 2484 CommandHistory::getInstance()->endCompoundOperation();
Chris@168 2485 }
Chris@168 2486
Chris@168 2487 void
Chris@142 2488 MainWindow::octaveShift(bool up)
Chris@142 2489 {
Chris@142 2490 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@142 2491
Chris@211 2492 CommandHistory::getInstance()->startCompoundOperation
Chris@211 2493 (up ? tr("Choose Higher Octave") : tr("Choose Lower Octave"), true);
Chris@142 2494
Chris@168 2495 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@168 2496 k != selections.end(); ++k) {
Chris@142 2497
Chris@168 2498 m_analyser->shiftOctave(*k, up);
Chris@252 2499 auxSnapNotes(*k);
Chris@142 2500 }
Chris@142 2501
Chris@142 2502 CommandHistory::getInstance()->endCompoundOperation();
Chris@142 2503 }
Chris@142 2504
Chris@142 2505 void
Chris@184 2506 MainWindow::togglePitchCandidates()
Chris@184 2507 {
Chris@199 2508 CommandHistory::getInstance()->startCompoundOperation(tr("Toggle Pitch Candidates"), true);
Chris@199 2509
Chris@184 2510 m_analyser->showPitchCandidates(!m_analyser->arePitchCandidatesShown());
Chris@199 2511
Chris@199 2512 CommandHistory::getInstance()->endCompoundOperation();
Chris@199 2513
Chris@187 2514 updateMenuStates();
Chris@184 2515 }
Chris@184 2516
Chris@184 2517 void
Chris@184 2518 MainWindow::switchPitchUp()
Chris@167 2519 {
Chris@211 2520 if (m_analyser->arePitchCandidatesShown()) {
Chris@211 2521 if (m_analyser->haveHigherPitchCandidate()) {
Chris@211 2522
Chris@211 2523 CommandHistory::getInstance()->startCompoundOperation
Chris@211 2524 (tr("Choose Higher Pitch Candidate"), true);
Chris@211 2525
Chris@211 2526 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@211 2527
Chris@211 2528 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@211 2529 k != selections.end(); ++k) {
Chris@211 2530 m_analyser->switchPitchCandidate(*k, true);
Chris@252 2531 auxSnapNotes(*k);
Chris@211 2532 }
Chris@211 2533
Chris@211 2534 CommandHistory::getInstance()->endCompoundOperation();
Chris@211 2535 }
Chris@211 2536 } else {
Chris@211 2537 octaveShift(true);
Chris@167 2538 }
Chris@167 2539 }
Chris@167 2540
Chris@167 2541 void
Chris@184 2542 MainWindow::switchPitchDown()
Chris@184 2543 {
Chris@211 2544 if (m_analyser->arePitchCandidatesShown()) {
Chris@211 2545 if (m_analyser->haveLowerPitchCandidate()) {
Chris@211 2546
Chris@211 2547 CommandHistory::getInstance()->startCompoundOperation
Chris@211 2548 (tr("Choose Lower Pitch Candidate"), true);
Chris@211 2549
Chris@211 2550 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@211 2551
Chris@211 2552 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@211 2553 k != selections.end(); ++k) {
Chris@211 2554 m_analyser->switchPitchCandidate(*k, false);
Chris@252 2555 auxSnapNotes(*k);
Chris@211 2556 }
Chris@211 2557
Chris@211 2558 CommandHistory::getInstance()->endCompoundOperation();
Chris@211 2559 }
Chris@211 2560 } else {
Chris@211 2561 octaveShift(false);
Chris@184 2562 }
Chris@184 2563 }
Chris@184 2564
Chris@184 2565 void
Chris@237 2566 MainWindow::snapNotesToPitches()
Chris@237 2567 {
matthiasm@274 2568 cerr << "in snapNotesToPitches" << endl;
Chris@237 2569 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@237 2570
Chris@237 2571 if (!selections.empty()) {
Chris@237 2572
Chris@237 2573 CommandHistory::getInstance()->startCompoundOperation
Chris@237 2574 (tr("Snap Notes to Pitches"), true);
Chris@237 2575
Chris@237 2576 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@237 2577 k != selections.end(); ++k) {
Chris@239 2578 auxSnapNotes(*k);
Chris@237 2579 }
Chris@237 2580
Chris@237 2581 CommandHistory::getInstance()->endCompoundOperation();
Chris@237 2582 }
Chris@237 2583 }
Chris@237 2584
Chris@237 2585 void
Chris@239 2586 MainWindow::auxSnapNotes(Selection s)
Chris@239 2587 {
matthiasm@274 2588 cerr << "in auxSnapNotes" << endl;
Chris@239 2589 FlexiNoteLayer *layer =
Chris@239 2590 qobject_cast<FlexiNoteLayer *>(m_analyser->getLayer(Analyser::Notes));
Chris@239 2591 if (!layer) return;
Chris@239 2592
Chris@239 2593 layer->snapSelectedNotesToPitchTrack(m_analyser->getPane(), s);
Chris@239 2594 }
Chris@239 2595
Chris@239 2596 void
Chris@240 2597 MainWindow::splitNote()
Chris@237 2598 {
Chris@237 2599 FlexiNoteLayer *layer =
Chris@237 2600 qobject_cast<FlexiNoteLayer *>(m_analyser->getLayer(Analyser::Notes));
Chris@237 2601 if (!layer) return;
Chris@237 2602
Chris@240 2603 layer->splitNotesAt(m_analyser->getPane(), m_viewManager->getPlaybackFrame());
Chris@237 2604 }
Chris@237 2605
Chris@237 2606 void
Chris@238 2607 MainWindow::mergeNotes()
Chris@238 2608 {
Chris@238 2609 FlexiNoteLayer *layer =
Chris@238 2610 qobject_cast<FlexiNoteLayer *>(m_analyser->getLayer(Analyser::Notes));
Chris@238 2611 if (!layer) return;
Chris@238 2612
Chris@238 2613 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@238 2614
Chris@238 2615 if (!selections.empty()) {
Chris@238 2616
Chris@238 2617 CommandHistory::getInstance()->startCompoundOperation
Chris@238 2618 (tr("Merge Notes"), true);
Chris@238 2619
Chris@238 2620 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@238 2621 k != selections.end(); ++k) {
Chris@240 2622 layer->mergeNotes(m_analyser->getPane(), *k, true);
Chris@238 2623 }
Chris@238 2624
Chris@238 2625 CommandHistory::getInstance()->endCompoundOperation();
Chris@238 2626 }
Chris@238 2627 }
Chris@238 2628
Chris@238 2629 void
matthiasm@292 2630 MainWindow::deleteNotes()
matthiasm@292 2631 {
matthiasm@292 2632 FlexiNoteLayer *layer =
matthiasm@292 2633 qobject_cast<FlexiNoteLayer *>(m_analyser->getLayer(Analyser::Notes));
matthiasm@292 2634 if (!layer) return;
matthiasm@292 2635
matthiasm@292 2636 MultiSelection::SelectionList selections = m_viewManager->getSelections();
matthiasm@292 2637
matthiasm@292 2638 if (!selections.empty()) {
matthiasm@292 2639
matthiasm@292 2640 CommandHistory::getInstance()->startCompoundOperation
matthiasm@292 2641 (tr("Delete Notes"), true);
matthiasm@292 2642
matthiasm@292 2643 for (MultiSelection::SelectionList::iterator k = selections.begin();
matthiasm@292 2644 k != selections.end(); ++k) {
matthiasm@294 2645 layer->deleteSelectionInclusive(*k);
matthiasm@292 2646 }
matthiasm@292 2647
matthiasm@292 2648 CommandHistory::getInstance()->endCompoundOperation();
matthiasm@292 2649 }
matthiasm@292 2650 }
matthiasm@292 2651
matthiasm@292 2652
matthiasm@292 2653 void
Chris@240 2654 MainWindow::formNoteFromSelection()
Chris@240 2655 {
Chris@571 2656 Pane *pane = m_analyser->getPane();
matthiasm@293 2657 Layer *layer0 = m_analyser->getLayer(Analyser::Notes);
Chris@573 2658 auto model = ModelById::getAs<NoteModel>(layer0->getModel());
Chris@571 2659 FlexiNoteLayer *layer = qobject_cast<FlexiNoteLayer *>(layer0);
Chris@573 2660 if (!layer || !model) return;
Chris@240 2661
Chris@240 2662 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@240 2663
Chris@240 2664 if (!selections.empty()) {
Chris@240 2665
Chris@240 2666 CommandHistory::getInstance()->startCompoundOperation
Chris@240 2667 (tr("Form Note from Selection"), true);
Chris@571 2668
Chris@240 2669 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@240 2670 k != selections.end(); ++k) {
Chris@571 2671
Chris@571 2672 // Chop existing events at start and end frames; remember
Chris@571 2673 // the first starting pitch, to use as default for new
Chris@571 2674 // note; delete existing events; create new note; ask
Chris@571 2675 // layer to merge, just in order to adapt the note to the
Chris@571 2676 // existing pitch track if possible. This way we should
Chris@571 2677 // handle all the possible cases of existing notes that
Chris@571 2678 // may or may not overlap the start or end times
Chris@571 2679
Chris@571 2680 sv_frame_t start = k->getStartFrame();
Chris@571 2681 sv_frame_t end = k->getEndFrame();
Chris@571 2682
Chris@571 2683 EventVector existing =
Chris@571 2684 model->getEventsStartingWithin(start, end - start);
Chris@571 2685
Chris@571 2686 int defaultPitch = 100;
Chris@571 2687 if (!existing.empty()) {
Chris@571 2688 defaultPitch = int(roundf(existing.begin()->getValue()));
matthiasm@293 2689 }
Chris@571 2690
Chris@571 2691 layer->splitNotesAt(pane, start);
Chris@571 2692 layer->splitNotesAt(pane, end);
Chris@571 2693 layer->deleteSelection(*k);
Chris@571 2694
Chris@571 2695 layer->addNoteOn(start, defaultPitch, 100);
Chris@571 2696 layer->addNoteOff(end, defaultPitch);
Chris@571 2697
Chris@571 2698 layer->mergeNotes(pane, *k, false);
Chris@240 2699 }
Chris@240 2700
matthiasm@293 2701 CommandHistory::getInstance()->endCompoundOperation();
Chris@240 2702 }
Chris@240 2703 }
Chris@240 2704
Chris@240 2705 void
Chris@0 2706 MainWindow::playSpeedChanged(int position)
Chris@0 2707 {
Chris@474 2708 PlaySpeedRangeMapper mapper;
Chris@0 2709
Chris@399 2710 double percent = m_playSpeed->mappedValue();
Chris@399 2711 double factor = mapper.getFactorForValue(percent);
Chris@0 2712
Chris@474 2713 int centre = m_playSpeed->defaultValue();
Chris@474 2714
Chris@474 2715 // Percentage is shown to 0dp if >100, to 1dp if <100; factor is
Chris@474 2716 // shown to 3sf
Chris@474 2717
Chris@474 2718 char pcbuf[30];
Chris@474 2719 char facbuf[30];
Chris@474 2720
Chris@474 2721 if (position == centre) {
Chris@0 2722 contextHelpChanged(tr("Playback speed: Normal"));
Chris@474 2723 } else if (position < centre) {
Chris@474 2724 sprintf(pcbuf, "%.1f", percent);
Chris@474 2725 sprintf(facbuf, "%.3g", 1.0 / factor);
Chris@474 2726 contextHelpChanged(tr("Playback speed: %1% (%2x slower)")
Chris@474 2727 .arg(pcbuf)
Chris@474 2728 .arg(facbuf));
Chris@0 2729 } else {
Chris@474 2730 sprintf(pcbuf, "%.0f", percent);
Chris@474 2731 sprintf(facbuf, "%.3g", factor);
Chris@474 2732 contextHelpChanged(tr("Playback speed: %1% (%2x faster)")
Chris@474 2733 .arg(pcbuf)
Chris@474 2734 .arg(facbuf));
Chris@0 2735 }
Chris@0 2736
Chris@474 2737 m_playSource->setTimeStretch(1.0 / factor); // factor is a speedup
Chris@0 2738
Chris@0 2739 updateMenuStates();
Chris@0 2740 }
Chris@0 2741
Chris@0 2742 void
Chris@0 2743 MainWindow::playSharpenToggled()
Chris@0 2744 {
Chris@0 2745 QSettings settings;
Chris@0 2746 settings.beginGroup("MainWindow");
Chris@0 2747 settings.setValue("playsharpen", m_playSharpen->isChecked());
Chris@0 2748 settings.endGroup();
Chris@0 2749
Chris@0 2750 playSpeedChanged(m_playSpeed->value());
justin@157 2751 // TODO: pitch gain?
Chris@0 2752 }
Chris@0 2753
Chris@0 2754 void
Chris@0 2755 MainWindow::playMonoToggled()
Chris@0 2756 {
Chris@0 2757 QSettings settings;
Chris@0 2758 settings.beginGroup("MainWindow");
Chris@0 2759 settings.setValue("playmono", m_playMono->isChecked());
Chris@0 2760 settings.endGroup();
Chris@0 2761
Chris@0 2762 playSpeedChanged(m_playSpeed->value());
justin@157 2763 // TODO: pitch gain?
Chris@0 2764 }
Chris@0 2765
Chris@0 2766 void
Chris@0 2767 MainWindow::speedUpPlayback()
Chris@0 2768 {
Chris@0 2769 int value = m_playSpeed->value();
Chris@0 2770 value = value + m_playSpeed->pageStep();
Chris@0 2771 if (value > m_playSpeed->maximum()) value = m_playSpeed->maximum();
Chris@0 2772 m_playSpeed->setValue(value);
Chris@0 2773 }
Chris@0 2774
Chris@0 2775 void
Chris@0 2776 MainWindow::slowDownPlayback()
Chris@0 2777 {
Chris@0 2778 int value = m_playSpeed->value();
Chris@0 2779 value = value - m_playSpeed->pageStep();
Chris@0 2780 if (value < m_playSpeed->minimum()) value = m_playSpeed->minimum();
Chris@0 2781 m_playSpeed->setValue(value);
Chris@0 2782 }
Chris@0 2783
Chris@0 2784 void
Chris@0 2785 MainWindow::restoreNormalPlayback()
Chris@0 2786 {
Chris@0 2787 m_playSpeed->setValue(m_playSpeed->defaultValue());
Chris@0 2788 }
Chris@0 2789
justin@157 2790 void
Chris@404 2791 MainWindow::audioGainChanged(float gain)
justin@159 2792 {
Chris@404 2793 double db = AudioLevel::multiplier_to_dB(gain);
Chris@404 2794 cerr << "gain = " << gain << " (" << db << " dB)" << endl;
Chris@404 2795 contextHelpChanged(tr("Audio Gain: %1 dB").arg(db));
Chris@417 2796 if (gain == 0.f) {
Chris@417 2797 m_analyser->setAudible(Analyser::Audio, false);
Chris@417 2798 } else {
Chris@417 2799 m_analyser->setAudible(Analyser::Audio, true);
Chris@417 2800 m_analyser->setGain(Analyser::Audio, gain);
Chris@417 2801 }
justin@159 2802 updateMenuStates();
justin@159 2803 }
justin@159 2804
justin@159 2805 void
Chris@404 2806 MainWindow::pitchGainChanged(float gain)
justin@159 2807 {
Chris@404 2808 double db = AudioLevel::multiplier_to_dB(gain);
Chris@404 2809 cerr << "gain = " << gain << " (" << db << " dB)" << endl;
Chris@404 2810 contextHelpChanged(tr("Pitch Gain: %1 dB").arg(db));
Chris@417 2811 if (gain == 0.f) {
Chris@417 2812 m_analyser->setAudible(Analyser::PitchTrack, false);
Chris@417 2813 } else {
Chris@417 2814 m_analyser->setAudible(Analyser::PitchTrack, true);
Chris@417 2815 m_analyser->setGain(Analyser::PitchTrack, gain);
Chris@417 2816 }
justin@157 2817 updateMenuStates();
justin@157 2818 }
justin@157 2819
justin@157 2820 void
Chris@404 2821 MainWindow::notesGainChanged(float gain)
justin@157 2822 {
Chris@404 2823 double db = AudioLevel::multiplier_to_dB(gain);
Chris@404 2824 cerr << "gain = " << gain << " (" << db << " dB)" << endl;
Chris@404 2825 contextHelpChanged(tr("Notes Gain: %1 dB").arg(db));
Chris@417 2826 if (gain == 0.f) {
Chris@417 2827 m_analyser->setAudible(Analyser::Notes, false);
Chris@417 2828 } else {
Chris@417 2829 m_analyser->setAudible(Analyser::Notes, true);
Chris@417 2830 m_analyser->setGain(Analyser::Notes, gain);
Chris@417 2831 }
justin@159 2832 updateMenuStates();
justin@159 2833 }
justin@159 2834
justin@159 2835 void
Chris@404 2836 MainWindow::audioPanChanged(float pan)
justin@159 2837 {
Chris@404 2838 contextHelpChanged(tr("Audio Pan: %1").arg(pan));
Chris@404 2839 m_analyser->setPan(Analyser::Audio, pan);
justin@160 2840 updateMenuStates();
justin@160 2841 }
justin@160 2842
justin@160 2843 void
Chris@404 2844 MainWindow::pitchPanChanged(float pan)
justin@160 2845 {
Chris@404 2846 contextHelpChanged(tr("Pitch Pan: %1").arg(pan));
Chris@404 2847 m_analyser->setPan(Analyser::PitchTrack, pan);
justin@160 2848 updateMenuStates();
justin@160 2849 }
justin@160 2850
justin@160 2851 void
Chris@404 2852 MainWindow::notesPanChanged(float pan)
justin@160 2853 {
Chris@404 2854 contextHelpChanged(tr("Notes Pan: %1").arg(pan));
Chris@404 2855 m_analyser->setPan(Analyser::Notes, pan);
justin@160 2856 updateMenuStates();
justin@160 2857 }
justin@160 2858
justin@160 2859 void
Chris@0 2860 MainWindow::updateVisibleRangeDisplay(Pane *p) const
Chris@0 2861 {
Chris@0 2862 if (!getMainModel() || !p) {
Chris@0 2863 return;
Chris@0 2864 }
Chris@0 2865
Chris@0 2866 bool haveSelection = false;
Chris@403 2867 sv_frame_t startFrame = 0, endFrame = 0;
Chris@0 2868
Chris@0 2869 if (m_viewManager && m_viewManager->haveInProgressSelection()) {
Chris@0 2870
Chris@0 2871 bool exclusive = false;
Chris@0 2872 Selection s = m_viewManager->getInProgressSelection(exclusive);
Chris@0 2873
Chris@0 2874 if (!s.isEmpty()) {
Chris@0 2875 haveSelection = true;
Chris@0 2876 startFrame = s.getStartFrame();
Chris@0 2877 endFrame = s.getEndFrame();
Chris@0 2878 }
Chris@0 2879 }
Chris@0 2880
Chris@0 2881 if (!haveSelection) {
Chris@0 2882 startFrame = p->getFirstVisibleFrame();
Chris@0 2883 endFrame = p->getLastVisibleFrame();
Chris@0 2884 }
Chris@0 2885
Chris@0 2886 RealTime start = RealTime::frame2RealTime
Chris@0 2887 (startFrame, getMainModel()->getSampleRate());
Chris@0 2888
Chris@0 2889 RealTime end = RealTime::frame2RealTime
Chris@0 2890 (endFrame, getMainModel()->getSampleRate());
Chris@0 2891
Chris@0 2892 RealTime duration = end - start;
Chris@0 2893
Chris@0 2894 QString startStr, endStr, durationStr;
Chris@0 2895 startStr = start.toText(true).c_str();
Chris@0 2896 endStr = end.toText(true).c_str();
Chris@0 2897 durationStr = duration.toText(true).c_str();
Chris@0 2898
Chris@0 2899 if (haveSelection) {
Chris@0 2900 m_myStatusMessage = tr("Selection: %1 to %2 (duration %3)")
Chris@0 2901 .arg(startStr).arg(endStr).arg(durationStr);
Chris@0 2902 } else {
Chris@0 2903 m_myStatusMessage = tr("Visible: %1 to %2 (duration %3)")
Chris@0 2904 .arg(startStr).arg(endStr).arg(durationStr);
Chris@0 2905 }
matthiasm@42 2906
Chris@362 2907 getStatusLabel()->setText(m_myStatusMessage);
Chris@0 2908 }
Chris@0 2909
Chris@0 2910 void
Chris@0 2911 MainWindow::updatePositionStatusDisplays() const
Chris@0 2912 {
Chris@0 2913 if (!statusBar()->isVisible()) return;
Chris@0 2914
Chris@0 2915 }
Chris@0 2916
Chris@0 2917 void
Chris@521 2918 MainWindow::monitoringLevelsChanged(float left, float right)
Chris@0 2919 {
Chris@0 2920 m_fader->setPeakLeft(left);
Chris@0 2921 m_fader->setPeakRight(right);
Chris@0 2922 }
Chris@0 2923
Chris@0 2924 void
Chris@399 2925 MainWindow::sampleRateMismatch(sv_samplerate_t ,
Chris@399 2926 sv_samplerate_t ,
Chris@399 2927 bool )
Chris@0 2928 {
Chris@0 2929 updateDescriptionLabel();
Chris@0 2930 }
Chris@0 2931
Chris@0 2932 void
Chris@0 2933 MainWindow::audioOverloadPluginDisabled()
Chris@0 2934 {
Chris@0 2935 QMessageBox::information
Chris@0 2936 (this, tr("Audio processing overload"),
Chris@0 2937 tr("<b>Overloaded</b><p>Audio effects plugin auditioning has been disabled due to a processing overload."));
Chris@0 2938 }
Chris@0 2939
Chris@0 2940 void
Chris@0 2941 MainWindow::audioTimeStretchMultiChannelDisabled()
Chris@0 2942 {
Chris@0 2943 static bool shownOnce = false;
Chris@0 2944 if (shownOnce) return;
Chris@0 2945 QMessageBox::information
Chris@0 2946 (this, tr("Audio processing overload"),
Chris@0 2947 tr("<b>Overloaded</b><p>Audio playback speed processing has been reduced to a single channel, due to a processing overload."));
Chris@0 2948 shownOnce = true;
Chris@0 2949 }
Chris@0 2950
Chris@0 2951 void
Chris@0 2952 MainWindow::layerRemoved(Layer *layer)
Chris@0 2953 {
Chris@0 2954 MainWindowBase::layerRemoved(layer);
Chris@0 2955 }
Chris@0 2956
Chris@0 2957 void
Chris@0 2958 MainWindow::layerInAView(Layer *layer, bool inAView)
Chris@0 2959 {
Chris@0 2960 MainWindowBase::layerInAView(layer, inAView);
Chris@0 2961 }
Chris@0 2962
Chris@0 2963 void
Chris@573 2964 MainWindow::modelAdded(ModelId model)
Chris@0 2965 {
Chris@0 2966 MainWindowBase::modelAdded(model);
Chris@573 2967 auto dtvm = ModelById::getAs<DenseTimeValueModel>(model);
Chris@0 2968 if (dtvm) {
Chris@70 2969 cerr << "A dense time-value model (such as an audio file) has been loaded" << endl;
Chris@0 2970 }
Chris@0 2971 }
Chris@0 2972
Chris@0 2973 void
Chris@573 2974 MainWindow::mainModelChanged(ModelId model)
Chris@0 2975 {
Chris@0 2976 m_panLayer->setModel(model);
Chris@0 2977
Chris@0 2978 MainWindowBase::mainModelChanged(model);
Chris@0 2979
Chris@479 2980 if (m_playTarget || m_audioIO) {
Chris@0 2981 connect(m_fader, SIGNAL(valueChanged(float)),
Chris@474 2982 this, SLOT(mainModelGainChanged(float)));
Chris@474 2983 }
Chris@474 2984 }
Chris@474 2985
Chris@474 2986 void
Chris@474 2987 MainWindow::mainModelGainChanged(float gain)
Chris@474 2988 {
Chris@474 2989 if (m_playTarget) {
Chris@474 2990 m_playTarget->setOutputGain(gain);
Chris@479 2991 } else if (m_audioIO) {
Chris@479 2992 m_audioIO->setOutputGain(gain);
Chris@0 2993 }
Chris@259 2994 }
Chris@259 2995
Chris@259 2996 void
Chris@323 2997 MainWindow::analyseNow()
Chris@323 2998 {
Chris@323 2999 cerr << "analyseNow called" << endl;
Chris@325 3000 if (!m_analyser) return;
Chris@325 3001
Chris@325 3002 CommandHistory::getInstance()->startCompoundOperation
Chris@325 3003 (tr("Analyse Audio"), true);
Chris@325 3004
Chris@325 3005 QString error = m_analyser->analyseExistingFile();
Chris@325 3006
Chris@325 3007 CommandHistory::getInstance()->endCompoundOperation();
Chris@325 3008
Chris@325 3009 if (error != "") {
Chris@325 3010 QMessageBox::warning
Chris@325 3011 (this,
Chris@325 3012 tr("Failed to analyse audio"),
Chris@325 3013 tr("<b>Analysis failed</b><p>%1</p>").arg(error),
Chris@325 3014 QMessageBox::Ok);
Chris@325 3015 }
Chris@323 3016 }
Chris@323 3017
Chris@323 3018 void
Chris@259 3019 MainWindow::analyseNewMainModel()
Chris@259 3020 {
Chris@573 3021 auto model = getMainModel();
Chris@260 3022
Chris@260 3023 cerr << "MainWindow::analyseNewMainModel: main model is " << model << endl;
Chris@260 3024
Chris@260 3025 cerr << "(document is " << m_document << ", it says main model is " << m_document->getMainModel() << ")" << endl;
Chris@259 3026
Chris@324 3027 if (!model) {
Chris@324 3028 cerr << "no main model!" << endl;
Chris@324 3029 return;
Chris@324 3030 }
Chris@324 3031
Chris@324 3032 if (!m_paneStack) {
Chris@324 3033 cerr << "no pane stack!" << endl;
Chris@324 3034 return;
Chris@324 3035 }
Chris@324 3036
Chris@324 3037 int pc = m_paneStack->getPaneCount();
Chris@324 3038 Pane *pane = 0;
Chris@324 3039 Pane *selectionStrip = 0;
Chris@324 3040
Chris@324 3041 if (pc < 2) {
Chris@586 3042 pane = m_paneStack->addPane(true);
Chris@586 3043 selectionStrip = m_paneStack->addPane(true);
Chris@324 3044 m_document->addLayerToView
Chris@324 3045 (selectionStrip,
Chris@324 3046 m_document->createMainModelLayer(LayerFactory::TimeRuler));
Chris@324 3047 } else {
Chris@324 3048 pane = m_paneStack->getPane(0);
Chris@324 3049 selectionStrip = m_paneStack->getPane(1);
Chris@324 3050 }
Chris@324 3051
Chris@362 3052 pane->setPlaybackFollow(PlaybackScrollPage);
Chris@362 3053
Chris@324 3054 if (selectionStrip) {
Chris@362 3055 selectionStrip->setPlaybackFollow(PlaybackScrollPage);
Chris@324 3056 selectionStrip->setFixedHeight(26);
Chris@324 3057 m_paneStack->sizePanesEqually();
Chris@324 3058 m_viewManager->clearToolModeOverrides();
Chris@324 3059 m_viewManager->setToolModeFor(selectionStrip,
Chris@324 3060 ViewManager::SelectMode);
Chris@324 3061 }
Chris@324 3062
Chris@324 3063 if (pane) {
Chris@324 3064
Chris@324 3065 disconnect(pane, SIGNAL(regionOutlined(QRect)),
Chris@324 3066 pane, SLOT(zoomToRegion(QRect)));
Chris@324 3067 connect(pane, SIGNAL(regionOutlined(QRect)),
Chris@324 3068 this, SLOT(regionOutlined(QRect)));
Chris@324 3069
Chris@324 3070 QString error = m_analyser->newFileLoaded
Chris@573 3071 (m_document, getMainModelId(), m_paneStack, pane);
Chris@324 3072 if (error != "") {
Chris@324 3073 QMessageBox::warning
Chris@324 3074 (this,
Chris@324 3075 tr("Failed to analyse audio"),
Chris@324 3076 tr("<b>Analysis failed</b><p>%1</p>").arg(error),
Chris@324 3077 QMessageBox::Ok);
Chris@72 3078 }
Chris@72 3079 }
matthiasm@366 3080
Chris@573 3081 if (!m_withSpectrogram) {
matthiasm@366 3082 m_analyser->setVisible(Analyser::Spectrogram, false);
matthiasm@366 3083 }
matthiasm@366 3084
Chris@573 3085 if (!m_withSonification) {
matthiasm@366 3086 m_analyser->setAudible(Analyser::PitchTrack, false);
matthiasm@366 3087 m_analyser->setAudible(Analyser::Notes, false);
matthiasm@366 3088 }
Chris@419 3089
Chris@419 3090 updateLayerStatuses();
Chris@487 3091 documentRestored();
Chris@0 3092 }
Chris@0 3093
Chris@0 3094 void
Chris@0 3095 MainWindow::modelGenerationFailed(QString transformName, QString message)
Chris@0 3096 {
Chris@0 3097 if (message != "") {
Chris@0 3098
Chris@0 3099 QMessageBox::warning
Chris@0 3100 (this,
Chris@0 3101 tr("Failed to generate layer"),
Chris@0 3102 tr("<b>Layer generation failed</b><p>Failed to generate derived layer.<p>The layer transform \"%1\" failed:<p>%2")
Chris@0 3103 .arg(transformName).arg(message),
Chris@0 3104 QMessageBox::Ok);
Chris@0 3105 } else {
Chris@0 3106 QMessageBox::warning
Chris@0 3107 (this,
Chris@0 3108 tr("Failed to generate layer"),
Chris@0 3109 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 3110 .arg(transformName),
Chris@0 3111 QMessageBox::Ok);
Chris@0 3112 }
Chris@0 3113 }
Chris@0 3114
Chris@0 3115 void
Chris@355 3116 MainWindow::modelGenerationWarning(QString /* transformName */, QString message)
Chris@0 3117 {
Chris@0 3118 QMessageBox::warning
Chris@0 3119 (this, tr("Warning"), message, QMessageBox::Ok);
Chris@0 3120 }
Chris@0 3121
Chris@0 3122 void
Chris@0 3123 MainWindow::modelRegenerationFailed(QString layerName,
Chris@399 3124 QString transformName,
Chris@399 3125 QString message)
Chris@0 3126 {
Chris@0 3127 if (message != "") {
Chris@0 3128
Chris@0 3129 QMessageBox::warning
Chris@0 3130 (this,
Chris@0 3131 tr("Failed to regenerate layer"),
Chris@0 3132 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 3133 .arg(layerName).arg(transformName).arg(message),
Chris@0 3134 QMessageBox::Ok);
Chris@0 3135 } else {
Chris@0 3136 QMessageBox::warning
Chris@0 3137 (this,
Chris@0 3138 tr("Failed to regenerate layer"),
Chris@0 3139 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 3140 .arg(layerName).arg(transformName),
Chris@0 3141 QMessageBox::Ok);
Chris@0 3142 }
Chris@0 3143 }
Chris@0 3144
Chris@0 3145 void
Chris@0 3146 MainWindow::modelRegenerationWarning(QString layerName,
Chris@355 3147 QString /* transformName */,
Chris@355 3148 QString message)
Chris@0 3149 {
Chris@0 3150 QMessageBox::warning
Chris@0 3151 (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 3152 }
Chris@0 3153
Chris@0 3154 void
Chris@520 3155 MainWindow::alignmentFailed(QString message)
Chris@0 3156 {
Chris@0 3157 QMessageBox::warning
Chris@0 3158 (this,
Chris@0 3159 tr("Failed to calculate alignment"),
Chris@520 3160 tr("<b>Alignment calculation failed</b><p>Failed to calculate an audio alignment:<p>%1")
Chris@520 3161 .arg(message),
Chris@0 3162 QMessageBox::Ok);
Chris@0 3163 }
Chris@0 3164
Chris@0 3165 void
Chris@0 3166 MainWindow::rightButtonMenuRequested(Pane *pane, QPoint position)
Chris@0 3167 {
Chris@70 3168 // cerr << "MainWindow::rightButtonMenuRequested(" << pane << ", " << position.x() << ", " << position.y() << ")" << endl;
Chris@0 3169 m_paneStack->setCurrentPane(pane);
Chris@0 3170 m_rightButtonMenu->popup(position);
Chris@0 3171 }
Chris@0 3172
Chris@0 3173 void
Chris@355 3174 MainWindow::handleOSCMessage(const OSCMessage &)
Chris@0 3175 {
Chris@70 3176 cerr << "MainWindow::handleOSCMessage: Not implemented" << endl;
Chris@0 3177 }
Chris@0 3178
Chris@0 3179 void
Chris@0 3180 MainWindow::mouseEnteredWidget()
Chris@0 3181 {
Chris@3 3182 QWidget *w = qobject_cast<QWidget *>(sender());
Chris@0 3183 if (!w) return;
Chris@0 3184
Chris@0 3185 if (w == m_fader) {
Chris@0 3186 contextHelpChanged(tr("Adjust the master playback level"));
Chris@0 3187 } else if (w == m_playSpeed) {
Chris@0 3188 contextHelpChanged(tr("Adjust the master playback speed"));
Chris@0 3189 } else if (w == m_playSharpen && w->isEnabled()) {
Chris@0 3190 contextHelpChanged(tr("Toggle transient sharpening for playback time scaling"));
Chris@0 3191 } else if (w == m_playMono && w->isEnabled()) {
Chris@0 3192 contextHelpChanged(tr("Toggle mono mode for playback time scaling"));
Chris@0 3193 }
Chris@0 3194 }
Chris@0 3195
Chris@0 3196 void
Chris@0 3197 MainWindow::mouseLeftWidget()
Chris@0 3198 {
Chris@0 3199 contextHelpChanged("");
Chris@0 3200 }
Chris@0 3201
Chris@0 3202 void
Chris@0 3203 MainWindow::help()
Chris@0 3204 {
Chris@4 3205 //!!! todo: help URL!
matthiasm@369 3206 openHelpUrl(tr("http://code.soundsoftware.ac.uk/projects/tony/wiki/Reference"));
Chris@0 3207 }
Chris@0 3208
Chris@0 3209 void
Chris@585 3210 MainWindow::whatsNew()
Chris@585 3211 {
Chris@585 3212 QFile changelog(":CHANGELOG");
Chris@585 3213 changelog.open(QFile::ReadOnly);
Chris@585 3214 QByteArray content = changelog.readAll();
Chris@585 3215 QString text = QString::fromUtf8(content);
Chris@585 3216
Chris@585 3217 QDialog *d = new QDialog(this);
Chris@585 3218 d->setWindowTitle(tr("What's New"));
Chris@585 3219
Chris@585 3220 QGridLayout *layout = new QGridLayout;
Chris@585 3221 d->setLayout(layout);
Chris@585 3222
Chris@585 3223 int row = 0;
Chris@585 3224
Chris@585 3225 QLabel *iconLabel = new QLabel;
Chris@585 3226 iconLabel->setPixmap(QApplication::windowIcon().pixmap(64, 64));
Chris@585 3227 layout->addWidget(iconLabel, row, 0);
Chris@585 3228
Chris@585 3229 layout->addWidget
Chris@585 3230 (new QLabel(tr("<h3>What's New in %1</h3>")
Chris@585 3231 .arg(QApplication::applicationName())),
Chris@585 3232 row++, 1);
Chris@585 3233 layout->setColumnStretch(2, 10);
Chris@585 3234
Chris@585 3235 QTextEdit *textEdit = new QTextEdit;
Chris@585 3236 layout->addWidget(textEdit, row++, 1, 1, 2);
Chris@585 3237
Chris@585 3238 if (m_newerVersionIs != "") {
Chris@585 3239 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 3240 }
Chris@585 3241
Chris@585 3242 QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Ok);
Chris@585 3243 layout->addWidget(bb, row++, 0, 1, 3);
Chris@585 3244 connect(bb, SIGNAL(accepted()), d, SLOT(accept()));
Chris@585 3245
Chris@585 3246 text.replace('\r', "");
Chris@585 3247 text.replace(QRegExp("(.)\n +(.)"), "\\1 \\2");
Chris@585 3248 text.replace(QRegExp("\n - ([^\n]+)"), "\n<li>\\1</li>");
Chris@585 3249 text.replace(QRegExp(": *\n"), ":\n<ul>\n");
Chris@585 3250 text.replace(QRegExp("</li>\n\\s*\n"), "</li>\n</ul>\n\n");
Chris@585 3251 text.replace(QRegExp("\n(\\w[^:\n]+:)"), "\n<p><b>\\1</b></p>");
Chris@585 3252 // text.replace(QRegExp("<li>([^,.\n]+)([,.] +\\w)"), "<li><b>\\1</b>\\2");
Chris@585 3253
Chris@585 3254 textEdit->setHtml(text);
Chris@585 3255 textEdit->setReadOnly(true);
Chris@585 3256
Chris@585 3257 d->setMinimumSize(m_viewManager->scalePixelSize(520),
Chris@585 3258 m_viewManager->scalePixelSize(450));
Chris@585 3259
Chris@585 3260 d->exec();
Chris@585 3261
Chris@585 3262 delete d;
Chris@585 3263 }
Chris@585 3264
Chris@585 3265 void
Chris@0 3266 MainWindow::about()
Chris@0 3267 {
Chris@0 3268 bool debug = false;
Chris@0 3269 QString version = "(unknown version)";
Chris@0 3270
Chris@0 3271 #ifdef BUILD_DEBUG
Chris@0 3272 debug = true;
Chris@0 3273 #endif
Chris@0 3274 version = tr("Release %1").arg(TONY_VERSION);
Chris@0 3275
Chris@0 3276 QString aboutText;
Chris@0 3277
Chris@0 3278 aboutText += tr("<h3>About Tony</h3>");
Chris@0 3279 aboutText += tr("<p>Tony is a program for interactive note and pitch analysis and annotation.</p>");
Chris@0 3280 aboutText += tr("<p>%1 : %2 configuration</p>")
Chris@0 3281 .arg(version)
Chris@0 3282 .arg(debug ? tr("Debug") : tr("Release"));
Chris@212 3283 aboutText += tr("<p>Using Qt framework version %1.</p>")
Chris@206 3284 .arg(QT_VERSION_STR);
Chris@0 3285
Chris@0 3286 aboutText +=
Chris@585 3287 "<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 3288 "<p>pYIN analysis plugin written by Matthias Mauch.</p>"
Chris@90 3289 "<p>This program is free software; you can redistribute it and/or "
Chris@90 3290 "modify it under the terms of the GNU General Public License as "
Chris@90 3291 "published by the Free Software Foundation; either version 2 of the "
Chris@0 3292 "License, or (at your option) any later version.<br>See the file "
Chris@0 3293 "COPYING included with this distribution for more information.</p>";
Chris@0 3294
Chris@585 3295 // use our own dialog so we can influence the size
Chris@585 3296
Chris@585 3297 QDialog *d = new QDialog(this);
Chris@585 3298
Chris@585 3299 d->setWindowTitle(tr("About %1").arg(QApplication::applicationName()));
Chris@585 3300
Chris@585 3301 QGridLayout *layout = new QGridLayout;
Chris@585 3302 d->setLayout(layout);
Chris@585 3303
Chris@585 3304 int row = 0;
Chris@585 3305
Chris@585 3306 QLabel *iconLabel = new QLabel;
Chris@585 3307 iconLabel->setPixmap(QApplication::windowIcon().pixmap(64, 64));
Chris@585 3308 layout->addWidget(iconLabel, row, 0, Qt::AlignTop);
Chris@585 3309
Chris@585 3310 QLabel *mainText = new QLabel();
Chris@585 3311 layout->addWidget(mainText, row, 1, 1, 2);
Chris@585 3312
Chris@585 3313 layout->setRowStretch(row, 10);
Chris@585 3314 layout->setColumnStretch(1, 10);
Chris@585 3315
Chris@585 3316 ++row;
Chris@585 3317
Chris@585 3318 QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Ok);
Chris@585 3319 layout->addWidget(bb, row++, 0, 1, 3);
Chris@585 3320 connect(bb, SIGNAL(accepted()), d, SLOT(accept()));
Chris@585 3321
Chris@585 3322 mainText->setWordWrap(true);
Chris@585 3323 mainText->setOpenExternalLinks(true);
Chris@585 3324 mainText->setText(aboutText);
Chris@585 3325
Chris@585 3326 d->setMinimumSize(m_viewManager->scalePixelSize(420),
Chris@585 3327 m_viewManager->scalePixelSize(200));
Chris@585 3328
Chris@585 3329 d->exec();
Chris@585 3330
Chris@585 3331 delete d;
Chris@0 3332 }
Chris@0 3333
Chris@0 3334 void
Chris@0 3335 MainWindow::keyReference()
Chris@0 3336 {
Chris@0 3337 m_keyReference->show();
Chris@0 3338 }
Chris@0 3339
Chris@231 3340 void
Chris@231 3341 MainWindow::newerVersionAvailable(QString version)
Chris@231 3342 {
Chris@585 3343 m_newerVersionIs = version;
Chris@585 3344
Chris@231 3345 //!!! nicer URL would be nicer
Chris@231 3346 QSettings settings;
Chris@231 3347 settings.beginGroup("NewerVersionWarning");
Chris@231 3348 QString tag = QString("version-%1-available-show").arg(version);
Chris@231 3349 if (settings.value(tag, true).toBool()) {
Chris@231 3350 QString title(tr("Newer version available"));
Chris@231 3351 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 3352 QMessageBox::information(this, title, text);
Chris@231 3353 settings.setValue(tag, false);
Chris@231 3354 }
Chris@231 3355 settings.endGroup();
Chris@231 3356 }
Chris@231 3357
matthiasm@356 3358 void
matthiasm@356 3359 MainWindow::ffwd()
matthiasm@356 3360 {
matthiasm@356 3361 if (!getMainModel()) return;
matthiasm@356 3362
Chris@403 3363 sv_frame_t frame = m_viewManager->getPlaybackFrame();
matthiasm@356 3364 ++frame;
matthiasm@356 3365
Chris@403 3366 sv_samplerate_t sr = getMainModel()->getSampleRate();
matthiasm@356 3367
matthiasm@356 3368 // The step is supposed to scale and be as wide as a step of
matthiasm@357 3369 // m_defaultFfwdRwdStep seconds at zoom level 720 and sr = 44100
Chris@540 3370
Chris@540 3371 ZoomLevel zoom = m_viewManager->getGlobalZoom();
Chris@540 3372 double framesPerPixel = 1.0;
Chris@540 3373 if (zoom.zone == ZoomLevel::FramesPerPixel) {
Chris@540 3374 framesPerPixel = zoom.level;
Chris@540 3375 } else {
Chris@540 3376 framesPerPixel = 1.0 / zoom.level;
Chris@540 3377 }
Chris@540 3378 double defaultFramesPerPixel = (720 * 44100) / sr;
Chris@540 3379 double scaler = framesPerPixel / defaultFramesPerPixel;
Chris@403 3380 RealTime step = m_defaultFfwdRwdStep * scaler;
Chris@403 3381
matthiasm@356 3382 frame = RealTime::realTime2Frame
Chris@403 3383 (RealTime::frame2RealTime(frame, sr) + step, sr);
Chris@403 3384
Chris@403 3385 if (frame > getMainModel()->getEndFrame()) {
matthiasm@356 3386 frame = getMainModel()->getEndFrame();
matthiasm@356 3387 }
Chris@403 3388
matthiasm@356 3389 if (frame < 0) frame = 0;
matthiasm@356 3390
matthiasm@356 3391 if (m_viewManager->getPlaySelectionMode()) {
Chris@403 3392 frame = m_viewManager->constrainFrameToSelection(frame);
matthiasm@356 3393 }
matthiasm@356 3394
matthiasm@356 3395 m_viewManager->setPlaybackFrame(frame);
matthiasm@356 3396
Chris@403 3397 if (frame == getMainModel()->getEndFrame() &&
matthiasm@356 3398 m_playSource &&
matthiasm@356 3399 m_playSource->isPlaying() &&
matthiasm@356 3400 !m_viewManager->getPlayLoopMode()) {
matthiasm@356 3401 stop();
matthiasm@356 3402 }
matthiasm@356 3403 }
matthiasm@356 3404
matthiasm@356 3405 void
matthiasm@356 3406 MainWindow::rewind()
matthiasm@356 3407 {
matthiasm@356 3408 if (!getMainModel()) return;
matthiasm@356 3409
Chris@403 3410 sv_frame_t frame = m_viewManager->getPlaybackFrame();
matthiasm@356 3411 if (frame > 0) --frame;
matthiasm@356 3412
Chris@403 3413 sv_samplerate_t sr = getMainModel()->getSampleRate();
matthiasm@356 3414
matthiasm@356 3415 // The step is supposed to scale and be as wide as a step of
matthiasm@357 3416 // m_defaultFfwdRwdStep seconds at zoom level 720 and sr = 44100
Chris@540 3417
Chris@540 3418 ZoomLevel zoom = m_viewManager->getGlobalZoom();
Chris@540 3419 double framesPerPixel = 1.0;
Chris@540 3420 if (zoom.zone == ZoomLevel::FramesPerPixel) {
Chris@540 3421 framesPerPixel = zoom.level;
Chris@540 3422 } else {
Chris@540 3423 framesPerPixel = 1.0 / zoom.level;
Chris@540 3424 }
Chris@540 3425 double defaultFramesPerPixel = (720 * 44100) / sr;
Chris@540 3426 double scaler = framesPerPixel / defaultFramesPerPixel;
Chris@403 3427 RealTime step = m_defaultFfwdRwdStep * scaler;
Chris@403 3428
matthiasm@356 3429 frame = RealTime::realTime2Frame
Chris@403 3430 (RealTime::frame2RealTime(frame, sr) - step, sr);
Chris@403 3431
Chris@403 3432 if (frame < getMainModel()->getStartFrame()) {
matthiasm@356 3433 frame = getMainModel()->getStartFrame();
matthiasm@356 3434 }
matthiasm@356 3435
matthiasm@356 3436 if (frame < 0) frame = 0;
matthiasm@356 3437
matthiasm@356 3438 if (m_viewManager->getPlaySelectionMode()) {
Chris@403 3439 frame = m_viewManager->constrainFrameToSelection(frame);
matthiasm@356 3440 }
matthiasm@356 3441
matthiasm@356 3442 m_viewManager->setPlaybackFrame(frame);
matthiasm@356 3443 }