annotate src/MainWindow.cpp @ 571:2cf4978eb724

Fix failure of "Form Note from Selection" to work correctly when an existing note spanned one end of the selection but did not reach the other. Also update subrepos to fix a deadlock
author Chris Cannam
date Thu, 20 Jun 2019 11:10:59 +0100
parents c6c8c1645ab6
children 5c14493026da
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"
matthiasm@26 30 #include "data/model/NoteModel.h"
Chris@0 31 #include "view/ViewManager.h"
Chris@0 32 #include "base/Preferences.h"
Chris@539 33 #include "base/RecordDirectory.h"
Chris@404 34 #include "base/AudioLevel.h"
Chris@0 35 #include "layer/WaveformLayer.h"
Chris@0 36 #include "layer/TimeInstantLayer.h"
Chris@0 37 #include "layer/TimeValueLayer.h"
Chris@192 38 #include "layer/SpectrogramLayer.h"
Chris@0 39 #include "widgets/Fader.h"
Chris@0 40 #include "view/Overview.h"
Chris@0 41 #include "widgets/AudioDial.h"
Chris@0 42 #include "widgets/IconLoader.h"
Chris@0 43 #include "widgets/KeyReference.h"
Chris@414 44 #include "widgets/LevelPanToolButton.h"
Chris@474 45 #include "audio/AudioCallbackPlaySource.h"
Chris@521 46 #include "audio/AudioCallbackRecordTarget.h"
Chris@474 47 #include "audio/PlaySpeedRangeMapper.h"
Chris@0 48 #include "base/Profiler.h"
Chris@0 49 #include "base/UnitDatabase.h"
Chris@0 50 #include "layer/ColourDatabase.h"
Chris@139 51 #include "base/Selection.h"
Chris@0 52
Chris@174 53 #include "rdf/RDFImporter.h"
Chris@174 54 #include "data/fileio/DataFileReaderFactory.h"
Chris@174 55 #include "data/fileio/CSVFormat.h"
matthiasm@26 56 #include "data/fileio/CSVFileWriter.h"
matthiasm@26 57 #include "data/fileio/MIDIFileWriter.h"
matthiasm@26 58 #include "rdf/RDFExporter.h"
matthiasm@26 59
Chris@227 60 #include "widgets/RangeInputDialog.h"
Chris@244 61 #include "widgets/ActivityLog.h"
Chris@227 62
Chris@0 63 // For version information
Chris@0 64 #include "vamp/vamp.h"
Chris@0 65 #include "vamp-sdk/PluginBase.h"
Chris@0 66 #include "plugin/api/ladspa.h"
Chris@0 67 #include "plugin/api/dssi.h"
Chris@0 68
Chris@474 69 #include <bqaudioio/SystemPlaybackTarget.h>
Chris@479 70 #include <bqaudioio/SystemAudioIO.h>
Chris@474 71
Chris@0 72 #include <QApplication>
Chris@0 73 #include <QMessageBox>
Chris@0 74 #include <QGridLayout>
Chris@0 75 #include <QLabel>
Chris@0 76 #include <QMenuBar>
Chris@0 77 #include <QToolBar>
Chris@0 78 #include <QToolButton>
Chris@0 79 #include <QInputDialog>
Chris@0 80 #include <QStatusBar>
Chris@0 81 #include <QFileInfo>
Chris@0 82 #include <QDir>
Chris@0 83 #include <QProcess>
Chris@2 84 #include <QPushButton>
Chris@0 85 #include <QSettings>
Chris@0 86 #include <QScrollArea>
Chris@128 87 #include <QPainter>
Chris@413 88 #include <QWidgetAction>
Chris@0 89
Chris@0 90 #include <iostream>
Chris@0 91 #include <cstdio>
Chris@0 92 #include <errno.h>
Chris@0 93
Chris@0 94 using std::vector;
Chris@0 95
Chris@0 96
Chris@474 97 MainWindow::MainWindow(SoundOptions options, bool withSonification, bool withSpectrogram) :
Chris@474 98 MainWindowBase(options),
Chris@0 99 m_overview(0),
Chris@0 100 m_mainMenusCreated(false),
Chris@0 101 m_playbackMenu(0),
gyorgyf@45 102 m_recentFilesMenu(0),
Chris@0 103 m_rightButtonMenu(0),
Chris@0 104 m_rightButtonPlaybackMenu(0),
Chris@0 105 m_deleteSelectedAction(0),
Chris@0 106 m_ffwdAction(0),
Chris@0 107 m_rwdAction(0),
Chris@168 108 m_intelligentActionOn(true), //GF: !!! temporary
Chris@244 109 m_activityLog(new ActivityLog()),
matthiasm@296 110 m_keyReference(new KeyReference()),
matthiasm@364 111 m_selectionAnchor(0),
matthiasm@364 112 m_withSonification(withSonification),
matthiasm@364 113 m_withSpectrogram(withSpectrogram)
Chris@0 114 {
Chris@0 115 setWindowTitle(QApplication::applicationName());
Chris@0 116
Chris@206 117 #ifdef Q_OS_MAC
Chris@206 118 #if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
Chris@206 119 setUnifiedTitleAndToolBarOnMac(true);
Chris@206 120 #endif
Chris@206 121 #endif
Chris@206 122
Chris@0 123 UnitDatabase *udb = UnitDatabase::getInstance();
Chris@0 124 udb->registerUnit("Hz");
Chris@0 125 udb->registerUnit("dB");
Chris@0 126 udb->registerUnit("s");
Chris@0 127
Chris@0 128 ColourDatabase *cdb = ColourDatabase::getInstance();
Chris@0 129 cdb->addColour(Qt::black, tr("Black"));
Chris@0 130 cdb->addColour(Qt::darkRed, tr("Red"));
Chris@0 131 cdb->addColour(Qt::darkBlue, tr("Blue"));
Chris@0 132 cdb->addColour(Qt::darkGreen, tr("Green"));
Chris@0 133 cdb->addColour(QColor(200, 50, 255), tr("Purple"));
Chris@0 134 cdb->addColour(QColor(255, 150, 50), tr("Orange"));
Chris@120 135 cdb->addColour(QColor(180, 180, 180), tr("Grey"));
Chris@0 136 cdb->setUseDarkBackground(cdb->addColour(Qt::white, tr("White")), true);
Chris@0 137 cdb->setUseDarkBackground(cdb->addColour(Qt::red, tr("Bright Red")), true);
Chris@0 138 cdb->setUseDarkBackground(cdb->addColour(QColor(30, 150, 255), tr("Bright Blue")), true);
Chris@0 139 cdb->setUseDarkBackground(cdb->addColour(Qt::green, tr("Bright Green")), true);
Chris@0 140 cdb->setUseDarkBackground(cdb->addColour(QColor(225, 74, 255), tr("Bright Purple")), true);
Chris@0 141 cdb->setUseDarkBackground(cdb->addColour(QColor(255, 188, 80), tr("Bright Orange")), true);
Chris@0 142
Chris@0 143 Preferences::getInstance()->setResampleOnLoad(true);
Chris@315 144 Preferences::getInstance()->setFixedSampleRate(44100);
Chris@0 145 Preferences::getInstance()->setSpectrogramSmoothing
Chris@0 146 (Preferences::SpectrogramInterpolated);
Chris@332 147 Preferences::getInstance()->setNormaliseAudio(true);
Chris@0 148
Chris@0 149 QSettings settings;
Chris@0 150
Chris@0 151 settings.beginGroup("MainWindow");
Chris@0 152 settings.setValue("showstatusbar", false);
Chris@0 153 settings.endGroup();
Chris@0 154
Chris@96 155 settings.beginGroup("Transformer");
Chris@96 156 settings.setValue("use-flexi-note-model", true);
Chris@96 157 settings.endGroup();
Chris@96 158
Chris@73 159 settings.beginGroup("LayerDefaults");
Chris@73 160 settings.setValue("waveform",
Chris@73 161 QString("<layer scale=\"%1\" channelMode=\"%2\"/>")
Chris@73 162 .arg(int(WaveformLayer::LinearScale))
Chris@73 163 .arg(int(WaveformLayer::MixChannels)));
Chris@73 164 settings.endGroup();
Chris@73 165
Chris@6 166 m_viewManager->setAlignMode(false);
Chris@6 167 m_viewManager->setPlaySoloMode(false);
Chris@392 168 m_viewManager->setToolMode(ViewManager::NavigateMode);
Chris@0 169 m_viewManager->setZoomWheelsEnabled(false);
Chris@6 170 m_viewManager->setIlluminateLocalFeatures(true);
matthiasm@368 171 m_viewManager->setShowWorkTitle(false);
Chris@6 172 m_viewManager->setShowCentreLine(false);
matthiasm@368 173 m_viewManager->setShowDuration(false);
Chris@213 174 m_viewManager->setOverlayMode(ViewManager::GlobalOverlays);
Chris@0 175
Chris@192 176 connect(m_viewManager, SIGNAL(selectionChangedByUser()),
Chris@192 177 this, SLOT(selectionChangedByUser()));
Chris@164 178
Chris@0 179 QFrame *frame = new QFrame;
Chris@0 180 setCentralWidget(frame);
Chris@0 181
Chris@0 182 QGridLayout *layout = new QGridLayout;
Chris@0 183
Chris@0 184 QScrollArea *scroll = new QScrollArea(frame);
Chris@0 185 scroll->setWidgetResizable(true);
Chris@0 186 scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
Chris@0 187 scroll->setFrameShape(QFrame::NoFrame);
Chris@0 188
Chris@6 189 // We have a pane stack: it comes with the territory. However, we
Chris@120 190 // have a fixed and known number of panes in it -- it isn't
Chris@120 191 // variable
Chris@0 192 m_paneStack->setLayoutStyle(PaneStack::NoPropertyStacks);
Chris@137 193 m_paneStack->setShowPaneAccessories(false);
Chris@399 194 connect(m_paneStack, SIGNAL(doubleClickSelectInvoked(sv_frame_t)),
Chris@399 195 this, SLOT(doubleClickSelectInvoked(sv_frame_t)));
Chris@0 196 scroll->setWidget(m_paneStack);
Chris@6 197
Chris@0 198 m_overview = new Overview(frame);
Chris@362 199 m_overview->setPlaybackFollow(PlaybackScrollPage);
Chris@0 200 m_overview->setViewManager(m_viewManager);
Chris@393 201 m_overview->setFixedHeight(60);
Chris@0 202 #ifndef _WIN32
Chris@0 203 // For some reason, the contents of the overview never appear if we
Chris@0 204 // make this setting on Windows. I have no inclination at the moment
Chris@0 205 // to track down the reason why.
Chris@0 206 m_overview->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
Chris@0 207 #endif
Chris@0 208 connect(m_overview, SIGNAL(contextHelpChanged(const QString &)),
Chris@0 209 this, SLOT(contextHelpChanged(const QString &)));
Chris@0 210
Chris@0 211 m_panLayer = new WaveformLayer;
Chris@0 212 m_panLayer->setChannelMode(WaveformLayer::MergeChannels);
Chris@0 213 m_panLayer->setAggressiveCacheing(true);
Chris@393 214 m_panLayer->setGain(0.5);
Chris@0 215 m_overview->addLayer(m_panLayer);
Chris@0 216
Chris@0 217 if (m_viewManager->getGlobalDarkBackground()) {
Chris@0 218 m_panLayer->setBaseColour
Chris@0 219 (ColourDatabase::getInstance()->getColourIndex(tr("Bright Green")));
Chris@0 220 } else {
Chris@0 221 m_panLayer->setBaseColour
matthiasm@13 222 (ColourDatabase::getInstance()->getColourIndex(tr("Blue")));
Chris@0 223 }
Chris@0 224
Chris@0 225 m_fader = new Fader(frame, false);
Chris@0 226 connect(m_fader, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
Chris@0 227 connect(m_fader, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
Chris@0 228
Chris@0 229 m_playSpeed = new AudioDial(frame);
Chris@343 230 m_playSpeed->setMeterColor(Qt::darkBlue);
Chris@0 231 m_playSpeed->setMinimum(0);
Chris@477 232 m_playSpeed->setMaximum(120);
Chris@502 233 m_playSpeed->setValue(60);
Chris@0 234 m_playSpeed->setFixedWidth(24);
Chris@0 235 m_playSpeed->setFixedHeight(24);
Chris@0 236 m_playSpeed->setNotchesVisible(true);
Chris@0 237 m_playSpeed->setPageStep(10);
Chris@477 238 m_playSpeed->setObjectName(tr("Playback Speed"));
Chris@477 239 m_playSpeed->setDefaultValue(60);
Chris@474 240 m_playSpeed->setRangeMapper(new PlaySpeedRangeMapper);
Chris@0 241 m_playSpeed->setShowToolTip(true);
Chris@0 242 connect(m_playSpeed, SIGNAL(valueChanged(int)),
gyorgyf@27 243 this, SLOT(playSpeedChanged(int)));
Chris@0 244 connect(m_playSpeed, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
Chris@0 245 connect(m_playSpeed, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
Chris@0 246
Chris@414 247 m_audioLPW = new LevelPanToolButton(frame);
Chris@434 248 m_audioLPW->setIncludeMute(false);
Chris@404 249 m_audioLPW->setObjectName(tr("Audio Track Level and Pan"));
Chris@404 250 connect(m_audioLPW, SIGNAL(levelChanged(float)), this, SLOT(audioGainChanged(float)));
Chris@404 251 connect(m_audioLPW, SIGNAL(panChanged(float)), this, SLOT(audioPanChanged(float)));
Chris@404 252
Chris@404 253 if (m_withSonification) {
Chris@404 254
Chris@414 255 m_pitchLPW = new LevelPanToolButton(frame);
Chris@434 256 m_pitchLPW->setIncludeMute(false);
Chris@404 257 m_pitchLPW->setObjectName(tr("Pitch Track Level and Pan"));
Chris@404 258 connect(m_pitchLPW, SIGNAL(levelChanged(float)), this, SLOT(pitchGainChanged(float)));
Chris@404 259 connect(m_pitchLPW, SIGNAL(panChanged(float)), this, SLOT(pitchPanChanged(float)));
Chris@404 260
Chris@414 261 m_notesLPW = new LevelPanToolButton(frame);
Chris@434 262 m_notesLPW->setIncludeMute(false);
Chris@404 263 m_notesLPW->setObjectName(tr("Note Track Level and Pan"));
Chris@404 264 connect(m_notesLPW, SIGNAL(levelChanged(float)), this, SLOT(notesGainChanged(float)));
Chris@404 265 connect(m_notesLPW, SIGNAL(panChanged(float)), this, SLOT(notesPanChanged(float)));
matthiasm@366 266 }
justin@160 267
Chris@0 268 layout->setSpacing(4);
Chris@6 269 layout->addWidget(m_overview, 0, 1);
Chris@6 270 layout->addWidget(scroll, 1, 1);
Chris@0 271
Chris@0 272 layout->setColumnStretch(1, 10);
Chris@0 273
Chris@0 274 frame->setLayout(layout);
Chris@0 275
gyorgyf@21 276 m_analyser = new Analyser();
Chris@128 277 connect(m_analyser, SIGNAL(layersChanged()),
Chris@128 278 this, SLOT(updateLayerStatuses()));
Chris@187 279 connect(m_analyser, SIGNAL(layersChanged()),
Chris@187 280 this, SLOT(updateMenuStates()));
gyorgyf@21 281
Chris@0 282 setupMenus();
Chris@0 283 setupToolbars();
Chris@0 284 setupHelpMenu();
Chris@0 285
Chris@0 286 statusBar();
Chris@0 287
Chris@288 288 finaliseMenus();
Chris@288 289
Chris@244 290 connect(m_viewManager, SIGNAL(activity(QString)),
Chris@244 291 m_activityLog, SLOT(activityHappened(QString)));
Chris@244 292 connect(m_playSource, SIGNAL(activity(QString)),
Chris@244 293 m_activityLog, SLOT(activityHappened(QString)));
Chris@244 294 connect(CommandHistory::getInstance(), SIGNAL(activity(QString)),
Chris@244 295 m_activityLog, SLOT(activityHappened(QString)));
Chris@244 296 connect(this, SIGNAL(activity(QString)),
Chris@244 297 m_activityLog, SLOT(activityHappened(QString)));
Chris@244 298 connect(this, SIGNAL(replacedDocument()), this, SLOT(documentReplaced()));
Chris@260 299 connect(this, SIGNAL(sessionLoaded()), this, SLOT(analyseNewMainModel()));
Chris@260 300 connect(this, SIGNAL(audioFileLoaded()), this, SLOT(analyseNewMainModel()));
Chris@244 301 m_activityLog->hide();
Chris@244 302
Chris@485 303 setAudioRecordMode(RecordReplaceSession);
Chris@485 304
Chris@95 305 newSession();
Chris@6 306
Chris@95 307 settings.beginGroup("MainWindow");
Chris@95 308 settings.setValue("zoom-default", 512);
Chris@95 309 settings.endGroup();
Chris@95 310 zoomDefault();
Chris@95 311
Chris@95 312 NetworkPermissionTester tester;
Chris@95 313 bool networkPermission = tester.havePermission();
Chris@95 314 if (networkPermission) {
Chris@95 315 m_versionTester = new VersionTester
Chris@95 316 ("sonicvisualiser.org", "latest-tony-version.txt", TONY_VERSION);
Chris@95 317 connect(m_versionTester, SIGNAL(newerVersionAvailable(QString)),
Chris@95 318 this, SLOT(newerVersionAvailable(QString)));
Chris@95 319 } else {
Chris@95 320 m_versionTester = 0;
Chris@95 321 }
Chris@0 322 }
Chris@0 323
Chris@0 324 MainWindow::~MainWindow()
Chris@0 325 {
Chris@6 326 delete m_analyser;
Chris@0 327 delete m_keyReference;
Chris@0 328 Profiles::getInstance()->dump();
Chris@0 329 }
Chris@0 330
Chris@0 331 void
Chris@0 332 MainWindow::setupMenus()
Chris@0 333 {
Chris@0 334 if (!m_mainMenusCreated) {
Chris@385 335
Chris@385 336 #ifdef Q_OS_LINUX
Chris@385 337 // In Ubuntu 14.04 the window's menu bar goes missing entirely
Chris@385 338 // if the user is running any desktop environment other than Unity
Chris@385 339 // (in which the faux single-menubar appears). The user has a
Chris@385 340 // workaround, to remove the appmenu-qt5 package, but that is
Chris@385 341 // awkward and the problem is so severe that it merits disabling
Chris@385 342 // the system menubar integration altogether. Like this:
Chris@385 343 menuBar()->setNativeMenuBar(false);
Chris@385 344 #endif
Chris@385 345
Chris@0 346 m_rightButtonMenu = new QMenu();
Chris@0 347 }
Chris@0 348
Chris@0 349 if (!m_mainMenusCreated) {
Chris@0 350 CommandHistory::getInstance()->registerMenu(m_rightButtonMenu);
Chris@0 351 m_rightButtonMenu->addSeparator();
Chris@0 352 }
Chris@0 353
Chris@0 354 setupFileMenu();
Chris@0 355 setupEditMenu();
Chris@0 356 setupViewMenu();
matthiasm@317 357 setupAnalysisMenu();
Chris@0 358
Chris@0 359 m_mainMenusCreated = true;
Chris@0 360 }
Chris@0 361
Chris@0 362 void
Chris@0 363 MainWindow::setupFileMenu()
Chris@0 364 {
Chris@0 365 if (m_mainMenusCreated) return;
Chris@0 366
Chris@0 367 QMenu *menu = menuBar()->addMenu(tr("&File"));
Chris@0 368 menu->setTearOffEnabled(true);
Chris@0 369 QToolBar *toolbar = addToolBar(tr("File Toolbar"));
Chris@0 370
Chris@0 371 m_keyReference->setCategory(tr("File and Session Management"));
Chris@0 372
Chris@0 373 IconLoader il;
Chris@1 374 QIcon icon;
Chris@1 375 QAction *action;
Chris@0 376
Chris@0 377 icon = il.load("fileopen");
Chris@1 378 action = new QAction(icon, tr("&Open..."), this);
Chris@0 379 action->setShortcut(tr("Ctrl+O"));
Chris@257 380 action->setStatusTip(tr("Open a session or audio file"));
Chris@0 381 connect(action, SIGNAL(triggered()), this, SLOT(openFile()));
Chris@0 382 m_keyReference->registerShortcut(action);
Chris@0 383 menu->addAction(action);
Chris@0 384 toolbar->addAction(action);
Chris@0 385
Chris@1 386 action = new QAction(tr("Open Lo&cation..."), this);
Chris@0 387 action->setShortcut(tr("Ctrl+Shift+O"));
Chris@1 388 action->setStatusTip(tr("Open a file from a remote URL"));
Chris@0 389 connect(action, SIGNAL(triggered()), this, SLOT(openLocation()));
Chris@0 390 m_keyReference->registerShortcut(action);
Chris@0 391 menu->addAction(action);
Chris@0 392
Chris@1 393 m_recentFilesMenu = menu->addMenu(tr("Open &Recent"));
Chris@0 394 m_recentFilesMenu->setTearOffEnabled(true);
Chris@0 395 setupRecentFilesMenu();
Chris@0 396 connect(&m_recentFiles, SIGNAL(recentChanged()),
Chris@0 397 this, SLOT(setupRecentFilesMenu()));
Chris@0 398
Chris@0 399 menu->addSeparator();
Chris@257 400
Chris@257 401 icon = il.load("filesave");
Chris@257 402 action = new QAction(icon, tr("&Save Session"), this);
Chris@257 403 action->setShortcut(tr("Ctrl+S"));
Chris@257 404 action->setStatusTip(tr("Save the current session into a %1 session file").arg(QApplication::applicationName()));
Chris@257 405 connect(action, SIGNAL(triggered()), this, SLOT(saveSession()));
Chris@257 406 connect(this, SIGNAL(canSave(bool)), action, SLOT(setEnabled(bool)));
Chris@257 407 m_keyReference->registerShortcut(action);
Chris@257 408 menu->addAction(action);
Chris@257 409 toolbar->addAction(action);
Chris@257 410
Chris@257 411 icon = il.load("filesaveas");
Chris@257 412 action = new QAction(icon, tr("Save Session &As..."), this);
Chris@257 413 action->setShortcut(tr("Ctrl+Shift+S"));
Chris@257 414 action->setStatusTip(tr("Save the current session into a new %1 session file").arg(QApplication::applicationName()));
Chris@257 415 connect(action, SIGNAL(triggered()), this, SLOT(saveSessionAs()));
Chris@313 416 connect(this, SIGNAL(canSaveAs(bool)), action, SLOT(setEnabled(bool)));
Chris@257 417 menu->addAction(action);
Chris@257 418 toolbar->addAction(action);
Chris@257 419
Chris@486 420 action = new QAction(tr("Save Session to Audio File &Path"), this);
matthiasm@310 421 action->setShortcut(tr("Ctrl+Alt+S"));
Chris@313 422 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 423 connect(action, SIGNAL(triggered()), this, SLOT(saveSessionInAudioPath()));
Chris@313 424 connect(this, SIGNAL(canSaveAs(bool)), action, SLOT(setEnabled(bool)));
matthiasm@310 425 menu->addAction(action);
matthiasm@310 426
Chris@257 427 menu->addSeparator();
Chris@257 428
Chris@253 429 action = new QAction(tr("I&mport Pitch Track Data..."), this);
Chris@253 430 action->setStatusTip(tr("Import pitch-track data from a CSV, RDF, or layer XML file"));
Chris@174 431 connect(action, SIGNAL(triggered()), this, SLOT(importPitchLayer()));
Chris@174 432 connect(this, SIGNAL(canImportLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@174 433 menu->addAction(action);
Chris@174 434
Chris@253 435 action = new QAction(tr("E&xport Pitch Track Data..."), this);
Chris@253 436 action->setStatusTip(tr("Export pitch-track data to a CSV, RDF, or layer XML file"));
Chris@85 437 connect(action, SIGNAL(triggered()), this, SLOT(exportPitchLayer()));
Chris@224 438 connect(this, SIGNAL(canExportPitchTrack(bool)), action, SLOT(setEnabled(bool)));
Chris@85 439 menu->addAction(action);
Chris@85 440
Chris@253 441 action = new QAction(tr("&Export Note Data..."), this);
Chris@253 442 action->setStatusTip(tr("Export note data to a CSV, RDF, layer XML, or MIDI file"));
Chris@85 443 connect(action, SIGNAL(triggered()), this, SLOT(exportNoteLayer()));
Chris@224 444 connect(this, SIGNAL(canExportNotes(bool)), action, SLOT(setEnabled(bool)));
matthiasm@26 445 menu->addAction(action);
matthiasm@26 446
matthiasm@26 447 menu->addSeparator();
Chris@485 448
Chris@485 449 action = new QAction(tr("Browse Recorded Audio"), this);
Chris@485 450 action->setStatusTip(tr("Open the Recorded Audio folder in the system file browser"));
Chris@485 451 connect(action, SIGNAL(triggered()), this, SLOT(browseRecordedAudio()));
Chris@485 452 menu->addAction(action);
Chris@485 453
Chris@485 454 menu->addSeparator();
Chris@485 455
Chris@0 456 action = new QAction(il.load("exit"), tr("&Quit"), this);
Chris@0 457 action->setShortcut(tr("Ctrl+Q"));
Chris@0 458 action->setStatusTip(tr("Exit %1").arg(QApplication::applicationName()));
Chris@0 459 connect(action, SIGNAL(triggered()), this, SLOT(close()));
Chris@0 460 m_keyReference->registerShortcut(action);
Chris@0 461 menu->addAction(action);
Chris@0 462 }
Chris@0 463
Chris@0 464 void
Chris@0 465 MainWindow::setupEditMenu()
Chris@0 466 {
Chris@0 467 if (m_mainMenusCreated) return;
Chris@0 468
Chris@0 469 QMenu *menu = menuBar()->addMenu(tr("&Edit"));
Chris@0 470 menu->setTearOffEnabled(true);
Chris@0 471 CommandHistory::getInstance()->registerMenu(menu);
Chris@126 472 menu->addSeparator();
Chris@126 473
Chris@254 474 m_keyReference->setCategory
Chris@254 475 (tr("Selection Strip Mouse Actions"));
Chris@254 476 m_keyReference->registerShortcut
Chris@254 477 (tr("Jump"), tr("Left"),
Chris@254 478 tr("Click left button to move the playback position to a time"));
Chris@254 479 m_keyReference->registerShortcut
Chris@254 480 (tr("Select"), tr("Left"),
Chris@254 481 tr("Click left button and drag to select a region of time"));
Chris@254 482 m_keyReference->registerShortcut
Chris@254 483 (tr("Select Note Duration"), tr("Double-Click Left"),
Chris@254 484 tr("Double-click left button to select the region of time corresponding to a note"));
Chris@254 485
Chris@126 486 QToolBar *toolbar = addToolBar(tr("Tools Toolbar"));
Chris@126 487
Chris@126 488 CommandHistory::getInstance()->registerToolbar(toolbar);
Chris@126 489
Chris@250 490 QActionGroup *group = new QActionGroup(this);
Chris@250 491
Chris@250 492 IconLoader il;
Chris@250 493
Chris@126 494 m_keyReference->setCategory(tr("Tool Selection"));
Chris@126 495 QAction *action = toolbar->addAction(il.load("navigate"),
Chris@126 496 tr("Navigate"));
Chris@126 497 action->setCheckable(true);
Chris@126 498 action->setChecked(true);
Chris@126 499 action->setShortcut(tr("1"));
Chris@126 500 action->setStatusTip(tr("Navigate"));
Chris@126 501 connect(action, SIGNAL(triggered()), this, SLOT(toolNavigateSelected()));
Chris@126 502 connect(this, SIGNAL(replacedDocument()), action, SLOT(trigger()));
Chris@126 503 group->addAction(action);
Chris@126 504 menu->addAction(action);
Chris@126 505 m_keyReference->registerShortcut(action);
Chris@126 506
Chris@250 507 m_keyReference->setCategory
Chris@250 508 (tr("Navigate Tool Mouse Actions"));
Chris@250 509 m_keyReference->registerShortcut
Chris@250 510 (tr("Navigate"), tr("Left"),
Chris@250 511 tr("Click left button and drag to move around"));
Chris@250 512 m_keyReference->registerShortcut
Chris@250 513 (tr("Re-Analyse Area"), tr("Shift+Left"),
Chris@250 514 tr("Shift-click left button and drag to define a specific pitch and time range to re-analyse"));
Chris@250 515 m_keyReference->registerShortcut
Chris@250 516 (tr("Edit"), tr("Double-Click Left"),
Chris@250 517 tr("Double-click left button on an item to edit it"));
Chris@392 518
Chris@250 519 m_keyReference->setCategory(tr("Tool Selection"));
Chris@126 520 action = toolbar->addAction(il.load("move"),
Chris@126 521 tr("Edit"));
Chris@126 522 action->setCheckable(true);
Chris@126 523 action->setShortcut(tr("2"));
Chris@126 524 action->setStatusTip(tr("Edit with Note Intelligence"));
Chris@126 525 connect(action, SIGNAL(triggered()), this, SLOT(toolEditSelected()));
Chris@126 526 group->addAction(action);
Chris@126 527 menu->addAction(action);
Chris@126 528 m_keyReference->registerShortcut(action);
Chris@126 529
Chris@250 530 m_keyReference->setCategory
Chris@250 531 (tr("Note Edit Tool Mouse Actions"));
Chris@250 532 m_keyReference->registerShortcut
Chris@250 533 (tr("Adjust Pitch"), tr("Left"),
Chris@250 534 tr("Click left button on the main part of a note and drag to move it up or down"));
Chris@250 535 m_keyReference->registerShortcut
Chris@250 536 (tr("Split"), tr("Left"),
Chris@250 537 tr("Click left button on the bottom edge of a note to split it at the click point"));
Chris@250 538 m_keyReference->registerShortcut
Chris@250 539 (tr("Resize"), tr("Left"),
Chris@250 540 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 541 m_keyReference->registerShortcut
Chris@250 542 (tr("Erase"), tr("Shift+Left"),
Chris@250 543 tr("Shift-click left button on a note to remove it"));
Chris@250 544
Chris@250 545
Chris@126 546 /* Remove for now...
Chris@126 547
Chris@250 548 m_keyReference->setCategory(tr("Tool Selection"));
Chris@126 549 action = toolbar->addAction(il.load("notes"),
Chris@126 550 tr("Free Edit"));
Chris@126 551 action->setCheckable(true);
Chris@126 552 action->setShortcut(tr("3"));
Chris@126 553 action->setStatusTip(tr("Free Edit"));
Chris@126 554 connect(action, SIGNAL(triggered()), this, SLOT(toolFreeEditSelected()));
Chris@126 555 group->addAction(action);
Chris@126 556 m_keyReference->registerShortcut(action);
Chris@126 557 */
Chris@142 558
Chris@142 559 menu->addSeparator();
Chris@142 560
Chris@143 561 m_keyReference->setCategory(tr("Selection"));
Chris@143 562
Chris@143 563 action = new QAction(tr("Select &All"), this);
Chris@143 564 action->setShortcut(tr("Ctrl+A"));
Chris@143 565 action->setStatusTip(tr("Select the whole duration of the current session"));
Chris@143 566 connect(action, SIGNAL(triggered()), this, SLOT(selectAll()));
Chris@143 567 connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool)));
Chris@143 568 m_keyReference->registerShortcut(action);
Chris@143 569 menu->addAction(action);
Chris@143 570 m_rightButtonMenu->addAction(action);
Chris@143 571
Chris@287 572 action = new QAction(tr("C&lear Selection"), this);
Chris@211 573 action->setShortcuts(QList<QKeySequence>()
Chris@211 574 << QKeySequence(tr("Esc"))
Chris@211 575 << QKeySequence(tr("Ctrl+Esc")));
Chris@211 576 action->setStatusTip(tr("Clear the selection and abandon any pending pitch choices in it"));
Chris@194 577 connect(action, SIGNAL(triggered()), this, SLOT(abandonSelection()));
Chris@143 578 connect(this, SIGNAL(canClearSelection(bool)), action, SLOT(setEnabled(bool)));
Chris@143 579 m_keyReference->registerShortcut(action);
Chris@211 580 m_keyReference->registerAlternativeShortcut(action, QKeySequence(tr("Ctrl+Esc")));
Chris@143 581 menu->addAction(action);
Chris@143 582 m_rightButtonMenu->addAction(action);
Chris@143 583
Chris@143 584 menu->addSeparator();
Chris@189 585 m_rightButtonMenu->addSeparator();
Chris@143 586
Chris@189 587 m_keyReference->setCategory(tr("Pitch Track"));
Chris@211 588
Chris@211 589 action = new QAction(tr("Choose Higher Pitch"), this);
Chris@211 590 action->setShortcut(tr("Ctrl+Up"));
Chris@211 591 action->setStatusTip(tr("Move pitches up an octave, or to the next higher pitch candidate"));
Chris@211 592 m_keyReference->registerShortcut(action);
Chris@211 593 connect(action, SIGNAL(triggered()), this, SLOT(switchPitchUp()));
Chris@211 594 connect(this, SIGNAL(canClearSelection(bool)), action, SLOT(setEnabled(bool)));
Chris@211 595 menu->addAction(action);
Chris@211 596 m_rightButtonMenu->addAction(action);
Chris@211 597
Chris@211 598 action = new QAction(tr("Choose Lower Pitch"), this);
Chris@211 599 action->setShortcut(tr("Ctrl+Down"));
Chris@211 600 action->setStatusTip(tr("Move pitches down an octave, or to the next lower pitch candidate"));
Chris@211 601 m_keyReference->registerShortcut(action);
Chris@211 602 connect(action, SIGNAL(triggered()), this, SLOT(switchPitchDown()));
Chris@211 603 connect(this, SIGNAL(canClearSelection(bool)), action, SLOT(setEnabled(bool)));
Chris@211 604 menu->addAction(action);
Chris@211 605 m_rightButtonMenu->addAction(action);
Chris@189 606
Chris@187 607 m_showCandidatesAction = new QAction(tr("Show Pitch Candidates"), this);
Chris@187 608 m_showCandidatesAction->setShortcut(tr("Ctrl+Return"));
Chris@189 609 m_showCandidatesAction->setStatusTip(tr("Toggle the display of alternative pitch candidates for the selected region"));
Chris@189 610 m_keyReference->registerShortcut(m_showCandidatesAction);
Chris@187 611 connect(m_showCandidatesAction, SIGNAL(triggered()), this, SLOT(togglePitchCandidates()));
Chris@187 612 connect(this, SIGNAL(canClearSelection(bool)), m_showCandidatesAction, SLOT(setEnabled(bool)));
Chris@187 613 menu->addAction(m_showCandidatesAction);
Chris@189 614 m_rightButtonMenu->addAction(m_showCandidatesAction);
Chris@187 615
Chris@287 616 action = new QAction(tr("Remove Pitches"), this);
matthiasm@327 617 action->setShortcut(tr("Ctrl+Backspace"));
Chris@211 618 action->setStatusTip(tr("Remove all pitch estimates within the selected region, making it unvoiced"));
Chris@189 619 m_keyReference->registerShortcut(action);
Chris@187 620 connect(action, SIGNAL(triggered()), this, SLOT(clearPitches()));
Chris@167 621 connect(this, SIGNAL(canClearSelection(bool)), action, SLOT(setEnabled(bool)));
Chris@167 622 menu->addAction(action);
Chris@189 623 m_rightButtonMenu->addAction(action);
Chris@237 624
Chris@237 625 menu->addSeparator();
Chris@237 626 m_rightButtonMenu->addSeparator();
Chris@237 627
matthiasm@327 628 m_keyReference->setCategory(tr("Note Track"));
matthiasm@327 629
Chris@287 630 action = new QAction(tr("Split Note"), this);
matthiasm@327 631 action->setShortcut(tr("/"));
Chris@240 632 action->setStatusTip(tr("Split the note at the current playback position into two"));
Chris@237 633 m_keyReference->registerShortcut(action);
Chris@240 634 connect(action, SIGNAL(triggered()), this, SLOT(splitNote()));
Chris@240 635 connect(this, SIGNAL(canExportNotes(bool)), action, SLOT(setEnabled(bool)));
Chris@237 636 menu->addAction(action);
Chris@237 637 m_rightButtonMenu->addAction(action);
Chris@238 638
Chris@287 639 action = new QAction(tr("Merge Notes"), this);
matthiasm@327 640 action->setShortcut(tr("\\"));
Chris@238 641 action->setStatusTip(tr("Merge all notes within the selected region into a single note"));
Chris@238 642 m_keyReference->registerShortcut(action);
Chris@238 643 connect(action, SIGNAL(triggered()), this, SLOT(mergeNotes()));
Chris@238 644 connect(this, SIGNAL(canSnapNotes(bool)), action, SLOT(setEnabled(bool)));
Chris@238 645 menu->addAction(action);
Chris@238 646 m_rightButtonMenu->addAction(action);
matthiasm@292 647
matthiasm@292 648 action = new QAction(tr("Delete Notes"), this);
matthiasm@327 649 action->setShortcut(tr("Backspace"));
matthiasm@327 650 action->setStatusTip(tr("Delete all notes within the selected region"));
matthiasm@292 651 m_keyReference->registerShortcut(action);
matthiasm@292 652 connect(action, SIGNAL(triggered()), this, SLOT(deleteNotes()));
matthiasm@292 653 connect(this, SIGNAL(canSnapNotes(bool)), action, SLOT(setEnabled(bool)));
matthiasm@292 654 menu->addAction(action);
matthiasm@292 655 m_rightButtonMenu->addAction(action);
Chris@238 656
Chris@240 657 action = new QAction(tr("Form Note from Selection"), this);
matthiasm@327 658 action->setShortcut(tr("="));
Chris@240 659 action->setStatusTip(tr("Form a note spanning the selected region, splitting any existing notes at its boundaries"));
Chris@240 660 m_keyReference->registerShortcut(action);
Chris@240 661 connect(action, SIGNAL(triggered()), this, SLOT(formNoteFromSelection()));
Chris@240 662 connect(this, SIGNAL(canSnapNotes(bool)), action, SLOT(setEnabled(bool)));
Chris@240 663 menu->addAction(action);
Chris@240 664 m_rightButtonMenu->addAction(action);
Chris@247 665
Chris@238 666 action = new QAction(tr("Snap Notes to Pitch Track"), this);
Chris@238 667 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 668 // m_keyReference->registerShortcut(action);
Chris@238 669 connect(action, SIGNAL(triggered()), this, SLOT(snapNotesToPitches()));
Chris@238 670 connect(this, SIGNAL(canSnapNotes(bool)), action, SLOT(setEnabled(bool)));
Chris@238 671 menu->addAction(action);
Chris@238 672 m_rightButtonMenu->addAction(action);
Chris@0 673 }
Chris@0 674
Chris@0 675 void
Chris@0 676 MainWindow::setupViewMenu()
Chris@0 677 {
Chris@0 678 if (m_mainMenusCreated) return;
Chris@0 679
Chris@0 680 IconLoader il;
Chris@0 681
Chris@0 682 QAction *action = 0;
Chris@0 683
Chris@0 684 m_keyReference->setCategory(tr("Panning and Navigation"));
Chris@0 685
Chris@0 686 QMenu *menu = menuBar()->addMenu(tr("&View"));
Chris@0 687 menu->setTearOffEnabled(true);
Chris@300 688 action = new QAction(tr("Peek &Left"), this);
Chris@300 689 action->setShortcut(tr("Alt+Left"));
Chris@300 690 action->setStatusTip(tr("Scroll the current pane to the left without changing the play position"));
Chris@0 691 connect(action, SIGNAL(triggered()), this, SLOT(scrollLeft()));
Chris@0 692 connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool)));
Chris@0 693 m_keyReference->registerShortcut(action);
Chris@0 694 menu->addAction(action);
gyorgyf@27 695
Chris@300 696 action = new QAction(tr("Peek &Right"), this);
Chris@300 697 action->setShortcut(tr("Alt+Right"));
Chris@300 698 action->setStatusTip(tr("Scroll the current pane to the right without changing the play position"));
Chris@0 699 connect(action, SIGNAL(triggered()), this, SLOT(scrollRight()));
Chris@0 700 connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool)));
Chris@0 701 m_keyReference->registerShortcut(action);
Chris@0 702 menu->addAction(action);
matthiasm@283 703
Chris@0 704 menu->addSeparator();
Chris@0 705
Chris@0 706 m_keyReference->setCategory(tr("Zoom"));
Chris@0 707
Chris@0 708 action = new QAction(il.load("zoom-in"),
Chris@0 709 tr("Zoom &In"), this);
Chris@0 710 action->setShortcut(tr("Up"));
Chris@0 711 action->setStatusTip(tr("Increase the zoom level"));
Chris@0 712 connect(action, SIGNAL(triggered()), this, SLOT(zoomIn()));
Chris@0 713 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
Chris@0 714 m_keyReference->registerShortcut(action);
Chris@0 715 menu->addAction(action);
gyorgyf@27 716
Chris@0 717 action = new QAction(il.load("zoom-out"),
Chris@0 718 tr("Zoom &Out"), this);
Chris@0 719 action->setShortcut(tr("Down"));
Chris@0 720 action->setStatusTip(tr("Decrease the zoom level"));
Chris@0 721 connect(action, SIGNAL(triggered()), this, SLOT(zoomOut()));
Chris@0 722 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
Chris@0 723 m_keyReference->registerShortcut(action);
Chris@0 724 menu->addAction(action);
gyorgyf@27 725
Chris@0 726 action = new QAction(tr("Restore &Default Zoom"), this);
Chris@0 727 action->setStatusTip(tr("Restore the zoom level to the default"));
Chris@0 728 connect(action, SIGNAL(triggered()), this, SLOT(zoomDefault()));
Chris@0 729 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
Chris@0 730 menu->addAction(action);
Chris@0 731
Chris@0 732 action = new QAction(il.load("zoom-fit"),
Chris@0 733 tr("Zoom to &Fit"), this);
Chris@0 734 action->setShortcut(tr("F"));
Chris@0 735 action->setStatusTip(tr("Zoom to show the whole file"));
Chris@0 736 connect(action, SIGNAL(triggered()), this, SLOT(zoomToFit()));
Chris@0 737 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
Chris@0 738 m_keyReference->registerShortcut(action);
Chris@0 739 menu->addAction(action);
Chris@227 740
Chris@227 741 menu->addSeparator();
Chris@227 742
Chris@227 743 action = new QAction(tr("Set Displayed Fre&quency Range..."), this);
Chris@227 744 action->setStatusTip(tr("Set the minimum and maximum frequencies in the visible display"));
Chris@227 745 connect(action, SIGNAL(triggered()), this, SLOT(editDisplayExtents()));
Chris@227 746 menu->addAction(action);
Chris@0 747 }
Chris@0 748
Chris@0 749 void
matthiasm@317 750 MainWindow::setupAnalysisMenu()
matthiasm@317 751 {
matthiasm@317 752 if (m_mainMenusCreated) return;
matthiasm@317 753
matthiasm@317 754 IconLoader il;
matthiasm@317 755
matthiasm@317 756 QAction *action = 0;
matthiasm@317 757
matthiasm@317 758 QMenu *menu = menuBar()->addMenu(tr("&Analysis"));
matthiasm@317 759 menu->setTearOffEnabled(true);
matthiasm@317 760
Chris@427 761 m_autoAnalyse = new QAction(tr("Auto-Analyse &New Audio"), this);
Chris@427 762 m_autoAnalyse->setStatusTip(tr("Automatically trigger analysis upon opening of a new audio file."));
Chris@427 763 m_autoAnalyse->setCheckable(true);
Chris@427 764 connect(m_autoAnalyse, SIGNAL(triggered()), this, SLOT(autoAnalysisToggled()));
Chris@427 765 menu->addAction(m_autoAnalyse);
Chris@427 766
Chris@427 767 action = new QAction(tr("&Analyse Now!"), this);
Chris@427 768 action->setStatusTip(tr("Trigger analysis of pitches and notes. (This will delete all existing pitches and notes.)"));
Chris@427 769 connect(action, SIGNAL(triggered()), this, SLOT(analyseNow()));
Chris@427 770 menu->addAction(action);
Chris@427 771 m_keyReference->registerShortcut(action);
Chris@427 772
Chris@427 773 menu->addSeparator();
Chris@427 774
Chris@427 775 m_precise = new QAction(tr("&Unbiased Timing (slow)"), this);
Chris@427 776 m_precise->setStatusTip(tr("Use a symmetric window in YIN to remove frequency-dependent timing bias. (This is slow!)"));
Chris@427 777 m_precise->setCheckable(true);
Chris@427 778 connect(m_precise, SIGNAL(triggered()), this, SLOT(precisionAnalysisToggled()));
Chris@427 779 menu->addAction(m_precise);
Chris@427 780
Chris@427 781 m_lowamp = new QAction(tr("&Penalise Soft Pitches"), this);
Chris@427 782 m_lowamp->setStatusTip(tr("Reduce the likelihood of detecting a pitch when the signal has low amplitude."));
Chris@427 783 m_lowamp->setCheckable(true);
Chris@427 784 connect(m_lowamp, SIGNAL(triggered()), this, SLOT(lowampAnalysisToggled()));
Chris@427 785 menu->addAction(m_lowamp);
Chris@427 786
Chris@427 787 m_onset = new QAction(tr("&High Onset Sensitivity"), this);
Chris@427 788 m_onset->setStatusTip(tr("Increase likelihood of separating notes, especially consecutive notes at the same pitch."));
Chris@427 789 m_onset->setCheckable(true);
Chris@427 790 connect(m_onset, SIGNAL(triggered()), this, SLOT(onsetAnalysisToggled()));
Chris@427 791 menu->addAction(m_onset);
Chris@427 792
Chris@427 793 m_prune = new QAction(tr("&Drop Short Notes"), this);
Chris@427 794 m_prune->setStatusTip(tr("Duration-based pruning: automatic note estimator will not output notes of less than 100ms duration."));
Chris@427 795 m_prune->setCheckable(true);
Chris@427 796 connect(m_prune, SIGNAL(triggered()), this, SLOT(pruneAnalysisToggled()));
Chris@427 797 menu->addAction(m_prune);
Chris@427 798
Chris@427 799 menu->addSeparator();
Chris@427 800
Chris@427 801 action = new QAction(tr("Reset Options to Defaults"), this);
Chris@427 802 action->setStatusTip(tr("Reset all of the Analyse menu options to their default settings."));
Chris@427 803 connect(action, SIGNAL(triggered()), this, SLOT(resetAnalyseOptions()));
Chris@427 804 menu->addAction(action);
Chris@427 805
Chris@427 806 updateAnalyseStates();
Chris@427 807 }
Chris@427 808
Chris@427 809 void
Chris@427 810 MainWindow::resetAnalyseOptions()
Chris@427 811 {
Chris@427 812 //!!! oh no, we need to update the menu states as well...
Chris@427 813 QSettings settings;
Chris@427 814 settings.beginGroup("Analyser");
Chris@427 815 settings.setValue("auto-analysis", true);
Chris@427 816 settings.setValue("precision-analysis", false);
Chris@427 817 settings.setValue("lowamp-analysis", true);
Chris@427 818 settings.setValue("onset-analysis", true);
Chris@427 819 settings.setValue("prune-analysis", true);
Chris@427 820 settings.endGroup();
Chris@427 821 updateAnalyseStates();
Chris@427 822 }
Chris@427 823
Chris@427 824 void
Chris@427 825 MainWindow::updateAnalyseStates()
Chris@427 826 {
Chris@323 827 QSettings settings;
Chris@323 828 settings.beginGroup("Analyser");
Chris@323 829 bool autoAnalyse = settings.value("auto-analysis", true).toBool();
Chris@323 830 bool precise = settings.value("precision-analysis", false).toBool();
matthiasm@345 831 bool lowamp = settings.value("lowamp-analysis", true).toBool();
matthiasm@423 832 bool onset = settings.value("onset-analysis", true).toBool();
matthiasm@423 833 bool prune = settings.value("prune-analysis", true).toBool();
Chris@323 834 settings.endGroup();
Chris@323 835
Chris@427 836 m_autoAnalyse->setChecked(autoAnalyse);
Chris@427 837 m_precise->setChecked(precise);
Chris@427 838 m_lowamp->setChecked(lowamp);
Chris@427 839 m_onset->setChecked(onset);
Chris@427 840 m_prune->setChecked(prune);
Chris@323 841 }
Chris@323 842
Chris@323 843 void
Chris@323 844 MainWindow::autoAnalysisToggled()
Chris@323 845 {
Chris@323 846 QAction *a = qobject_cast<QAction *>(sender());
Chris@323 847 if (!a) return;
Chris@323 848
Chris@323 849 bool set = a->isChecked();
Chris@323 850
Chris@323 851 QSettings settings;
Chris@323 852 settings.beginGroup("Analyser");
Chris@323 853 settings.setValue("auto-analysis", set);
Chris@323 854 settings.endGroup();
Chris@323 855 }
Chris@323 856
Chris@323 857 void
Chris@323 858 MainWindow::precisionAnalysisToggled()
Chris@323 859 {
Chris@323 860 QAction *a = qobject_cast<QAction *>(sender());
Chris@323 861 if (!a) return;
Chris@323 862
Chris@323 863 bool set = a->isChecked();
Chris@323 864
Chris@323 865 QSettings settings;
Chris@323 866 settings.beginGroup("Analyser");
Chris@323 867 settings.setValue("precision-analysis", set);
Chris@323 868 settings.endGroup();
Chris@323 869
Chris@326 870 // don't run analyseNow() automatically -- it's a destructive operation
matthiasm@317 871 }
matthiasm@317 872
matthiasm@317 873 void
matthiasm@345 874 MainWindow::lowampAnalysisToggled()
matthiasm@345 875 {
matthiasm@345 876 QAction *a = qobject_cast<QAction *>(sender());
matthiasm@345 877 if (!a) return;
matthiasm@345 878
matthiasm@345 879 bool set = a->isChecked();
matthiasm@345 880
matthiasm@345 881 QSettings settings;
matthiasm@345 882 settings.beginGroup("Analyser");
matthiasm@345 883 settings.setValue("lowamp-analysis", set);
matthiasm@345 884 settings.endGroup();
matthiasm@345 885
matthiasm@345 886 // don't run analyseNow() automatically -- it's a destructive operation
matthiasm@345 887 }
matthiasm@345 888
matthiasm@345 889 void
matthiasm@423 890 MainWindow::onsetAnalysisToggled()
matthiasm@423 891 {
matthiasm@423 892 QAction *a = qobject_cast<QAction *>(sender());
matthiasm@423 893 if (!a) return;
matthiasm@423 894
matthiasm@423 895 bool set = a->isChecked();
matthiasm@423 896
matthiasm@423 897 QSettings settings;
matthiasm@423 898 settings.beginGroup("Analyser");
matthiasm@423 899 settings.setValue("onset-analysis", set);
matthiasm@423 900 settings.endGroup();
matthiasm@423 901
matthiasm@423 902 // don't run analyseNow() automatically -- it's a destructive operation
matthiasm@423 903 }
matthiasm@423 904
matthiasm@423 905 void
matthiasm@423 906 MainWindow::pruneAnalysisToggled()
matthiasm@423 907 {
matthiasm@423 908 QAction *a = qobject_cast<QAction *>(sender());
matthiasm@423 909 if (!a) return;
matthiasm@423 910
matthiasm@423 911 bool set = a->isChecked();
matthiasm@423 912
matthiasm@423 913 QSettings settings;
matthiasm@423 914 settings.beginGroup("Analyser");
matthiasm@423 915 settings.setValue("prune-analysis", set);
matthiasm@423 916 settings.endGroup();
matthiasm@423 917
matthiasm@423 918 // don't run analyseNow() automatically -- it's a destructive operation
matthiasm@423 919 }
matthiasm@423 920
matthiasm@423 921 void
Chris@0 922 MainWindow::setupHelpMenu()
Chris@0 923 {
Chris@0 924 QMenu *menu = menuBar()->addMenu(tr("&Help"));
Chris@0 925 menu->setTearOffEnabled(true);
Chris@0 926
Chris@0 927 m_keyReference->setCategory(tr("Help"));
Chris@0 928
Chris@0 929 IconLoader il;
Chris@0 930
Chris@1 931 QString name = QApplication::applicationName();
matthiasm@367 932 QAction *action;
matthiasm@367 933
Chris@0 934 action = new QAction(tr("&Key and Mouse Reference"), this);
Chris@0 935 action->setShortcut(tr("F2"));
Chris@1 936 action->setStatusTip(tr("Open a window showing the keystrokes you can use in %1").arg(name));
Chris@0 937 connect(action, SIGNAL(triggered()), this, SLOT(keyReference()));
Chris@0 938 m_keyReference->registerShortcut(action);
Chris@0 939 menu->addAction(action);
matthiasm@369 940
matthiasm@369 941 action = new QAction(il.load("help"),
matthiasm@369 942 tr("&Help Reference"), this);
matthiasm@369 943 action->setShortcut(tr("F1"));
matthiasm@369 944 action->setStatusTip(tr("Open the %1 reference manual").arg(name));
matthiasm@369 945 connect(action, SIGNAL(triggered()), this, SLOT(help()));
matthiasm@369 946 m_keyReference->registerShortcut(action);
matthiasm@369 947 menu->addAction(action);
matthiasm@369 948
Chris@0 949
Chris@1 950 action = new QAction(tr("%1 on the &Web").arg(name), this);
Chris@1 951 action->setStatusTip(tr("Open the %1 website").arg(name));
Chris@0 952 connect(action, SIGNAL(triggered()), this, SLOT(website()));
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@570 1316 NoteModel *model = qobject_cast<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@0 1648 Pane *pane = m_paneStack->addPane();
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@85 2114 Model *model = layer->getModel();
Chris@85 2115
Chris@85 2116 QFile file(path);
Chris@85 2117 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
Chris@85 2118 return tr("Failed to open file %1 for writing").arg(path);
Chris@85 2119 } else {
Chris@85 2120 QTextStream out(&file);
Chris@85 2121 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
Chris@85 2122 << "<!DOCTYPE sonic-visualiser>\n"
Chris@85 2123 << "<sv>\n"
Chris@85 2124 << " <data>\n";
Chris@85 2125
Chris@85 2126 model->toXml(out, " ");
Chris@85 2127
Chris@85 2128 out << " </data>\n"
Chris@85 2129 << " <display>\n";
Chris@85 2130
Chris@85 2131 layer->toXml(out, " ");
Chris@85 2132
Chris@85 2133 out << " </display>\n"
Chris@85 2134 << "</sv>\n";
Chris@85 2135
Chris@85 2136 return "";
Chris@85 2137 }
Chris@85 2138 }
Chris@85 2139
Chris@0 2140 void
Chris@174 2141 MainWindow::importPitchLayer()
Chris@174 2142 {
Chris@174 2143 QString path = getOpenFileName(FileFinder::LayerFileNoMidiNonSV);
Chris@174 2144 if (path == "") return;
Chris@174 2145
Chris@174 2146 FileOpenStatus status = importPitchLayer(path);
Chris@174 2147
Chris@174 2148 if (status == FileOpenFailed) {
Chris@174 2149 emit hideSplash();
Chris@174 2150 QMessageBox::critical(this, tr("Failed to open file"),
Chris@174 2151 tr("<b>File open failed</b><p>Layer file %1 could not be opened.").arg(path));
Chris@174 2152 return;
Chris@174 2153 } else if (status == FileOpenWrongMode) {
Chris@174 2154 emit hideSplash();
Chris@174 2155 QMessageBox::critical(this, tr("Failed to open file"),
Chris@174 2156 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 2157 }
Chris@174 2158 }
Chris@174 2159
Chris@174 2160 MainWindow::FileOpenStatus
Chris@174 2161 MainWindow::importPitchLayer(FileSource source)
Chris@174 2162 {
Chris@174 2163 if (!source.isAvailable()) return FileOpenFailed;
Chris@174 2164 source.waitForData();
Chris@174 2165
Chris@442 2166 if (!waitForInitialAnalysis()) return FileOpenCancelled;
Chris@442 2167
Chris@174 2168 QString path = source.getLocalFilename();
Chris@174 2169
Chris@174 2170 RDFImporter::RDFDocumentType rdfType =
Chris@174 2171 RDFImporter::identifyDocumentType(QUrl::fromLocalFile(path).toString());
Chris@174 2172
Chris@174 2173 if (rdfType != RDFImporter::NotRDF) {
Chris@174 2174
Chris@174 2175 //!!!
Chris@174 2176 return FileOpenFailed;
Chris@174 2177
Chris@174 2178 } else if (source.getExtension().toLower() == "svl" ||
Chris@174 2179 (source.getExtension().toLower() == "xml" &&
Chris@174 2180 (SVFileReader::identifyXmlFile(source.getLocalFilename())
Chris@174 2181 == SVFileReader::SVLayerFile))) {
Chris@174 2182
Chris@174 2183 //!!!
Chris@174 2184 return FileOpenFailed;
Chris@174 2185
Chris@174 2186 } else {
Chris@174 2187
Chris@174 2188 try {
Chris@174 2189
Chris@174 2190 CSVFormat format(path);
Chris@174 2191 format.setSampleRate(getMainModel()->getSampleRate());
Chris@174 2192
Chris@174 2193 if (format.getModelType() != CSVFormat::TwoDimensionalModel) {
Chris@174 2194 //!!! error report
Chris@174 2195 return FileOpenFailed;
Chris@174 2196 }
Chris@174 2197
Chris@174 2198 Model *model = DataFileReaderFactory::loadCSV
Chris@174 2199 (path, format, getMainModel()->getSampleRate());
Chris@174 2200
Chris@174 2201 if (model) {
Chris@174 2202
Chris@174 2203 SVDEBUG << "MainWindow::importPitchLayer: Have model" << endl;
Chris@174 2204
Chris@174 2205 CommandHistory::getInstance()->startCompoundOperation
Chris@174 2206 (tr("Import Pitch Track"), true);
Chris@174 2207
Chris@174 2208 Layer *newLayer = m_document->createImportedLayer(model);
Chris@174 2209
Chris@174 2210 m_analyser->takePitchTrackFrom(newLayer);
Chris@174 2211
Chris@174 2212 m_document->deleteLayer(newLayer);
Chris@174 2213
Chris@174 2214 CommandHistory::getInstance()->endCompoundOperation();
Chris@174 2215
Chris@174 2216 //!!! swap all data in to existing layer instead of this
Chris@174 2217
Chris@174 2218 if (!source.isRemote()) {
Chris@174 2219 registerLastOpenedFilePath
Chris@174 2220 (FileFinder::LayerFile,
Chris@174 2221 path); // for file dialog
Chris@174 2222 }
Chris@174 2223
Chris@174 2224 return FileOpenSucceeded;
Chris@174 2225 }
Chris@174 2226 } catch (DataFileReaderFactory::Exception e) {
Chris@174 2227 if (e == DataFileReaderFactory::ImportCancelled) {
Chris@174 2228 return FileOpenCancelled;
Chris@174 2229 }
Chris@174 2230 }
Chris@174 2231 }
Chris@174 2232
Chris@174 2233 return FileOpenFailed;
Chris@174 2234 }
Chris@174 2235
Chris@174 2236 void
Chris@85 2237 MainWindow::exportPitchLayer()
matthiasm@26 2238 {
Chris@174 2239 Layer *layer = m_analyser->getLayer(Analyser::PitchTrack);
matthiasm@26 2240 if (!layer) return;
matthiasm@26 2241
Chris@85 2242 SparseTimeValueModel *model =
Chris@85 2243 qobject_cast<SparseTimeValueModel *>(layer->getModel());
matthiasm@26 2244 if (!model) return;
matthiasm@26 2245
Chris@96 2246 FileFinder::FileType type = FileFinder::LayerFileNoMidiNonSV;
matthiasm@26 2247
matthiasm@26 2248 QString path = getSaveFileName(type);
matthiasm@26 2249
matthiasm@26 2250 if (path == "") return;
matthiasm@26 2251
Chris@442 2252 if (!waitForInitialAnalysis()) return;
Chris@442 2253
matthiasm@26 2254 if (QFileInfo(path).suffix() == "") path += ".svl";
matthiasm@26 2255
matthiasm@26 2256 QString suffix = QFileInfo(path).suffix().toLower();
matthiasm@26 2257
matthiasm@26 2258 QString error;
matthiasm@26 2259
matthiasm@26 2260 if (suffix == "xml" || suffix == "svl") {
matthiasm@26 2261
Chris@85 2262 error = exportToSVL(path, layer);
matthiasm@26 2263
matthiasm@26 2264 } else if (suffix == "ttl" || suffix == "n3") {
matthiasm@26 2265
Chris@85 2266 RDFExporter exporter(path, model);
Chris@85 2267 exporter.write();
Chris@85 2268 if (!exporter.isOK()) {
Chris@85 2269 error = exporter.getError();
Chris@85 2270 }
Chris@85 2271
Chris@85 2272 } else {
Chris@85 2273
Chris@429 2274 DataExportOptions options = DataExportFillGaps;
Chris@429 2275
Chris@85 2276 CSVFileWriter writer(path, model,
Chris@429 2277 ((suffix == "csv") ? "," : "\t"),
Chris@429 2278 options);
Chris@85 2279 writer.write();
Chris@85 2280
Chris@85 2281 if (!writer.isOK()) {
Chris@85 2282 error = writer.getError();
Chris@85 2283 }
Chris@85 2284 }
Chris@85 2285
Chris@85 2286 if (error != "") {
Chris@85 2287 QMessageBox::critical(this, tr("Failed to write file"), error);
Chris@85 2288 } else {
Chris@85 2289 emit activity(tr("Export layer to \"%1\"").arg(path));
Chris@85 2290 }
Chris@85 2291 }
Chris@85 2292
Chris@85 2293 void
Chris@85 2294 MainWindow::exportNoteLayer()
Chris@85 2295 {
Chris@174 2296 Layer *layer = m_analyser->getLayer(Analyser::Notes);
Chris@85 2297 if (!layer) return;
Chris@85 2298
Chris@570 2299 NoteModel *model = qobject_cast<NoteModel *>(layer->getModel());
Chris@85 2300 if (!model) return;
Chris@85 2301
Chris@96 2302 FileFinder::FileType type = FileFinder::LayerFileNonSV;
Chris@85 2303
Chris@85 2304 QString path = getSaveFileName(type);
Chris@85 2305
Chris@85 2306 if (path == "") return;
Chris@85 2307
Chris@85 2308 if (QFileInfo(path).suffix() == "") path += ".svl";
Chris@85 2309
Chris@85 2310 QString suffix = QFileInfo(path).suffix().toLower();
Chris@85 2311
Chris@85 2312 QString error;
Chris@85 2313
Chris@85 2314 if (suffix == "xml" || suffix == "svl") {
Chris@85 2315
Chris@85 2316 error = exportToSVL(path, layer);
Chris@85 2317
Chris@85 2318 } else if (suffix == "mid" || suffix == "midi") {
Chris@85 2319
Chris@85 2320 MIDIFileWriter writer(path, model, model->getSampleRate());
Chris@85 2321 writer.write();
Chris@85 2322 if (!writer.isOK()) {
Chris@85 2323 error = writer.getError();
Chris@85 2324 }
Chris@85 2325
Chris@85 2326 } else if (suffix == "ttl" || suffix == "n3") {
Chris@85 2327
Chris@85 2328 RDFExporter exporter(path, model);
Chris@85 2329 exporter.write();
Chris@85 2330 if (!exporter.isOK()) {
Chris@85 2331 error = exporter.getError();
matthiasm@26 2332 }
matthiasm@26 2333
matthiasm@26 2334 } else {
matthiasm@26 2335
Chris@429 2336 DataExportOptions options = DataExportOmitLevels;
Chris@429 2337
matthiasm@26 2338 CSVFileWriter writer(path, model,
Chris@429 2339 ((suffix == "csv") ? "," : "\t"),
Chris@429 2340 options);
matthiasm@26 2341 writer.write();
matthiasm@26 2342
matthiasm@26 2343 if (!writer.isOK()) {
matthiasm@26 2344 error = writer.getError();
matthiasm@26 2345 }
matthiasm@26 2346 }
matthiasm@26 2347
matthiasm@26 2348 if (error != "") {
matthiasm@26 2349 QMessageBox::critical(this, tr("Failed to write file"), error);
matthiasm@26 2350 } else {
matthiasm@26 2351 emit activity(tr("Export layer to \"%1\"").arg(path));
matthiasm@26 2352 }
matthiasm@26 2353 }
matthiasm@26 2354
Chris@139 2355 void
Chris@485 2356 MainWindow::browseRecordedAudio()
Chris@485 2357 {
Chris@485 2358 if (!m_recordTarget) return;
Chris@485 2359
Chris@539 2360 QString path = RecordDirectory::getRecordContainerDirectory();
Chris@539 2361 if (path == "") path = RecordDirectory::getRecordDirectory();
Chris@485 2362 if (path == "") return;
Chris@485 2363
Chris@485 2364 openLocalFolder(path);
Chris@485 2365 }
Chris@485 2366
Chris@485 2367 void
Chris@399 2368 MainWindow::doubleClickSelectInvoked(sv_frame_t frame)
Chris@139 2369 {
Chris@399 2370 sv_frame_t f0, f1;
Chris@139 2371 m_analyser->getEnclosingSelectionScope(frame, f0, f1);
Chris@139 2372
Chris@139 2373 cerr << "MainWindow::doubleClickSelectInvoked(" << frame << "): [" << f0 << "," << f1 << "]" << endl;
Chris@139 2374
Chris@164 2375 Selection sel(f0, f1);
Chris@164 2376 m_viewManager->setSelection(sel);
Chris@164 2377 }
Chris@164 2378
Chris@164 2379 void
Chris@194 2380 MainWindow::abandonSelection()
Chris@167 2381 {
Chris@194 2382 // Named abandonSelection rather than clearSelection to indicate
Chris@194 2383 // that this is an active operation -- it restores the original
Chris@194 2384 // content of the pitch track in the selected region rather than
Chris@194 2385 // simply un-selecting.
Chris@194 2386
Chris@194 2387 cerr << "MainWindow::abandonSelection()" << endl;
Chris@167 2388
Chris@199 2389 CommandHistory::getInstance()->startCompoundOperation(tr("Abandon Selection"), true);
Chris@168 2390
Chris@194 2391 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@194 2392 if (!selections.empty()) {
Chris@194 2393 Selection sel = *selections.begin();
Chris@199 2394 m_analyser->abandonReAnalysis(sel);
Chris@252 2395 auxSnapNotes(sel);
Chris@194 2396 }
Chris@194 2397
Chris@167 2398 MainWindowBase::clearSelection();
Chris@168 2399
Chris@168 2400 CommandHistory::getInstance()->endCompoundOperation();
Chris@167 2401 }
Chris@167 2402
Chris@167 2403 void
Chris@192 2404 MainWindow::selectionChangedByUser()
Chris@164 2405 {
Chris@221 2406 if (!m_document) {
Chris@221 2407 // we're exiting, most likely
Chris@221 2408 return;
Chris@221 2409 }
Chris@221 2410
Chris@164 2411 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@164 2412
Chris@192 2413 cerr << "MainWindow::selectionChangedByUser" << endl;
Chris@192 2414
Chris@192 2415 m_analyser->showPitchCandidates(m_pendingConstraint.isConstrained());
Chris@190 2416
Chris@164 2417 if (!selections.empty()) {
Chris@164 2418 Selection sel = *selections.begin();
Chris@192 2419 cerr << "MainWindow::selectionChangedByUser: have selection" << endl;
Chris@192 2420 QString error = m_analyser->reAnalyseSelection
Chris@192 2421 (sel, m_pendingConstraint);
Chris@164 2422 if (error != "") {
Chris@164 2423 QMessageBox::critical
Chris@164 2424 (this, tr("Failed to analyse selection"),
Chris@164 2425 tr("<b>Analysis failed</b><p>%2</p>").arg(error));
Chris@164 2426 }
Chris@164 2427 }
Chris@192 2428
Chris@192 2429 m_pendingConstraint = Analyser::FrequencyRange();
Chris@192 2430 }
Chris@192 2431
Chris@192 2432 void
Chris@192 2433 MainWindow::regionOutlined(QRect r)
Chris@192 2434 {
Chris@192 2435 cerr << "MainWindow::regionOutlined(" << r.x() << "," << r.y() << "," << r.width() << "," << r.height() << ")" << endl;
Chris@192 2436
Chris@192 2437 Pane *pane = qobject_cast<Pane *>(sender());
Chris@192 2438 if (!pane) {
Chris@192 2439 cerr << "MainWindow::regionOutlined: not sent by pane, ignoring" << endl;
Chris@192 2440 return;
Chris@192 2441 }
Chris@192 2442
Chris@192 2443 if (!m_analyser) {
Chris@192 2444 cerr << "MainWindow::regionOutlined: no analyser, ignoring" << endl;
Chris@192 2445 return;
Chris@192 2446 }
Chris@192 2447
Chris@192 2448 SpectrogramLayer *spectrogram = qobject_cast<SpectrogramLayer *>
Chris@192 2449 (m_analyser->getLayer(Analyser::Spectrogram));
Chris@192 2450 if (!spectrogram) {
Chris@192 2451 cerr << "MainWindow::regionOutlined: no spectrogram layer, ignoring" << endl;
Chris@192 2452 return;
Chris@192 2453 }
Chris@192 2454
Chris@399 2455 sv_frame_t f0 = pane->getFrameForX(r.x());
Chris@399 2456 sv_frame_t f1 = pane->getFrameForX(r.x() + r.width());
Chris@192 2457
Chris@399 2458 double v0 = spectrogram->getFrequencyForY(pane, r.y() + r.height());
Chris@399 2459 double v1 = spectrogram->getFrequencyForY(pane, r.y());
Chris@192 2460
Chris@192 2461 cerr << "MainWindow::regionOutlined: frame " << f0 << " -> " << f1
Chris@192 2462 << ", frequency " << v0 << " -> " << v1 << endl;
Chris@192 2463
Chris@192 2464 m_pendingConstraint = Analyser::FrequencyRange(v0, v1);
Chris@192 2465
Chris@192 2466 Selection sel(f0, f1);
Chris@192 2467 m_viewManager->setSelection(sel);
Chris@0 2468 }
Chris@0 2469
Chris@0 2470 void
Chris@168 2471 MainWindow::clearPitches()
Chris@168 2472 {
Chris@168 2473 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@168 2474
Chris@168 2475 CommandHistory::getInstance()->startCompoundOperation(tr("Clear Pitches"), true);
Chris@168 2476
Chris@168 2477 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@168 2478 k != selections.end(); ++k) {
Chris@184 2479 m_analyser->deletePitches(*k);
Chris@252 2480 auxSnapNotes(*k);
Chris@168 2481 }
Chris@168 2482
Chris@168 2483 CommandHistory::getInstance()->endCompoundOperation();
Chris@168 2484 }
Chris@168 2485
Chris@168 2486 void
Chris@142 2487 MainWindow::octaveShift(bool up)
Chris@142 2488 {
Chris@142 2489 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@142 2490
Chris@211 2491 CommandHistory::getInstance()->startCompoundOperation
Chris@211 2492 (up ? tr("Choose Higher Octave") : tr("Choose Lower Octave"), true);
Chris@142 2493
Chris@168 2494 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@168 2495 k != selections.end(); ++k) {
Chris@142 2496
Chris@168 2497 m_analyser->shiftOctave(*k, up);
Chris@252 2498 auxSnapNotes(*k);
Chris@142 2499 }
Chris@142 2500
Chris@142 2501 CommandHistory::getInstance()->endCompoundOperation();
Chris@142 2502 }
Chris@142 2503
Chris@142 2504 void
Chris@184 2505 MainWindow::togglePitchCandidates()
Chris@184 2506 {
Chris@199 2507 CommandHistory::getInstance()->startCompoundOperation(tr("Toggle Pitch Candidates"), true);
Chris@199 2508
Chris@184 2509 m_analyser->showPitchCandidates(!m_analyser->arePitchCandidatesShown());
Chris@199 2510
Chris@199 2511 CommandHistory::getInstance()->endCompoundOperation();
Chris@199 2512
Chris@187 2513 updateMenuStates();
Chris@184 2514 }
Chris@184 2515
Chris@184 2516 void
Chris@184 2517 MainWindow::switchPitchUp()
Chris@167 2518 {
Chris@211 2519 if (m_analyser->arePitchCandidatesShown()) {
Chris@211 2520 if (m_analyser->haveHigherPitchCandidate()) {
Chris@211 2521
Chris@211 2522 CommandHistory::getInstance()->startCompoundOperation
Chris@211 2523 (tr("Choose Higher Pitch Candidate"), true);
Chris@211 2524
Chris@211 2525 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@211 2526
Chris@211 2527 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@211 2528 k != selections.end(); ++k) {
Chris@211 2529 m_analyser->switchPitchCandidate(*k, true);
Chris@252 2530 auxSnapNotes(*k);
Chris@211 2531 }
Chris@211 2532
Chris@211 2533 CommandHistory::getInstance()->endCompoundOperation();
Chris@211 2534 }
Chris@211 2535 } else {
Chris@211 2536 octaveShift(true);
Chris@167 2537 }
Chris@167 2538 }
Chris@167 2539
Chris@167 2540 void
Chris@184 2541 MainWindow::switchPitchDown()
Chris@184 2542 {
Chris@211 2543 if (m_analyser->arePitchCandidatesShown()) {
Chris@211 2544 if (m_analyser->haveLowerPitchCandidate()) {
Chris@211 2545
Chris@211 2546 CommandHistory::getInstance()->startCompoundOperation
Chris@211 2547 (tr("Choose Lower Pitch Candidate"), true);
Chris@211 2548
Chris@211 2549 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@211 2550
Chris@211 2551 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@211 2552 k != selections.end(); ++k) {
Chris@211 2553 m_analyser->switchPitchCandidate(*k, false);
Chris@252 2554 auxSnapNotes(*k);
Chris@211 2555 }
Chris@211 2556
Chris@211 2557 CommandHistory::getInstance()->endCompoundOperation();
Chris@211 2558 }
Chris@211 2559 } else {
Chris@211 2560 octaveShift(false);
Chris@184 2561 }
Chris@184 2562 }
Chris@184 2563
Chris@184 2564 void
Chris@237 2565 MainWindow::snapNotesToPitches()
Chris@237 2566 {
matthiasm@274 2567 cerr << "in snapNotesToPitches" << endl;
Chris@237 2568 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@237 2569
Chris@237 2570 if (!selections.empty()) {
Chris@237 2571
Chris@237 2572 CommandHistory::getInstance()->startCompoundOperation
Chris@237 2573 (tr("Snap Notes to Pitches"), true);
Chris@237 2574
Chris@237 2575 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@237 2576 k != selections.end(); ++k) {
Chris@239 2577 auxSnapNotes(*k);
Chris@237 2578 }
Chris@237 2579
Chris@237 2580 CommandHistory::getInstance()->endCompoundOperation();
Chris@237 2581 }
Chris@237 2582 }
Chris@237 2583
Chris@237 2584 void
Chris@239 2585 MainWindow::auxSnapNotes(Selection s)
Chris@239 2586 {
matthiasm@274 2587 cerr << "in auxSnapNotes" << endl;
Chris@239 2588 FlexiNoteLayer *layer =
Chris@239 2589 qobject_cast<FlexiNoteLayer *>(m_analyser->getLayer(Analyser::Notes));
Chris@239 2590 if (!layer) return;
Chris@239 2591
Chris@239 2592 layer->snapSelectedNotesToPitchTrack(m_analyser->getPane(), s);
Chris@239 2593 }
Chris@239 2594
Chris@239 2595 void
Chris@240 2596 MainWindow::splitNote()
Chris@237 2597 {
Chris@237 2598 FlexiNoteLayer *layer =
Chris@237 2599 qobject_cast<FlexiNoteLayer *>(m_analyser->getLayer(Analyser::Notes));
Chris@237 2600 if (!layer) return;
Chris@237 2601
Chris@240 2602 layer->splitNotesAt(m_analyser->getPane(), m_viewManager->getPlaybackFrame());
Chris@237 2603 }
Chris@237 2604
Chris@237 2605 void
Chris@238 2606 MainWindow::mergeNotes()
Chris@238 2607 {
Chris@238 2608 FlexiNoteLayer *layer =
Chris@238 2609 qobject_cast<FlexiNoteLayer *>(m_analyser->getLayer(Analyser::Notes));
Chris@238 2610 if (!layer) return;
Chris@238 2611
Chris@238 2612 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@238 2613
Chris@238 2614 if (!selections.empty()) {
Chris@238 2615
Chris@238 2616 CommandHistory::getInstance()->startCompoundOperation
Chris@238 2617 (tr("Merge Notes"), true);
Chris@238 2618
Chris@238 2619 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@238 2620 k != selections.end(); ++k) {
Chris@240 2621 layer->mergeNotes(m_analyser->getPane(), *k, true);
Chris@238 2622 }
Chris@238 2623
Chris@238 2624 CommandHistory::getInstance()->endCompoundOperation();
Chris@238 2625 }
Chris@238 2626 }
Chris@238 2627
Chris@238 2628 void
matthiasm@292 2629 MainWindow::deleteNotes()
matthiasm@292 2630 {
matthiasm@292 2631 FlexiNoteLayer *layer =
matthiasm@292 2632 qobject_cast<FlexiNoteLayer *>(m_analyser->getLayer(Analyser::Notes));
matthiasm@292 2633 if (!layer) return;
matthiasm@292 2634
matthiasm@292 2635 MultiSelection::SelectionList selections = m_viewManager->getSelections();
matthiasm@292 2636
matthiasm@292 2637 if (!selections.empty()) {
matthiasm@292 2638
matthiasm@292 2639 CommandHistory::getInstance()->startCompoundOperation
matthiasm@292 2640 (tr("Delete Notes"), true);
matthiasm@292 2641
matthiasm@292 2642 for (MultiSelection::SelectionList::iterator k = selections.begin();
matthiasm@292 2643 k != selections.end(); ++k) {
matthiasm@294 2644 layer->deleteSelectionInclusive(*k);
matthiasm@292 2645 }
matthiasm@292 2646
matthiasm@292 2647 CommandHistory::getInstance()->endCompoundOperation();
matthiasm@292 2648 }
matthiasm@292 2649 }
matthiasm@292 2650
matthiasm@292 2651
matthiasm@292 2652 void
Chris@240 2653 MainWindow::formNoteFromSelection()
Chris@240 2654 {
Chris@571 2655 Pane *pane = m_analyser->getPane();
matthiasm@293 2656 Layer *layer0 = m_analyser->getLayer(Analyser::Notes);
Chris@570 2657 NoteModel *model = qobject_cast<NoteModel *>(layer0->getModel());
Chris@571 2658 FlexiNoteLayer *layer = qobject_cast<FlexiNoteLayer *>(layer0);
Chris@240 2659 if (!layer) return;
Chris@240 2660
Chris@240 2661 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@240 2662
Chris@240 2663 if (!selections.empty()) {
Chris@240 2664
Chris@240 2665 CommandHistory::getInstance()->startCompoundOperation
Chris@240 2666 (tr("Form Note from Selection"), true);
Chris@571 2667
Chris@240 2668 for (MultiSelection::SelectionList::iterator k = selections.begin();
Chris@240 2669 k != selections.end(); ++k) {
Chris@571 2670
Chris@571 2671 // Chop existing events at start and end frames; remember
Chris@571 2672 // the first starting pitch, to use as default for new
Chris@571 2673 // note; delete existing events; create new note; ask
Chris@571 2674 // layer to merge, just in order to adapt the note to the
Chris@571 2675 // existing pitch track if possible. This way we should
Chris@571 2676 // handle all the possible cases of existing notes that
Chris@571 2677 // may or may not overlap the start or end times
Chris@571 2678
Chris@571 2679 sv_frame_t start = k->getStartFrame();
Chris@571 2680 sv_frame_t end = k->getEndFrame();
Chris@571 2681
Chris@571 2682 EventVector existing =
Chris@571 2683 model->getEventsStartingWithin(start, end - start);
Chris@571 2684
Chris@571 2685 int defaultPitch = 100;
Chris@571 2686 if (!existing.empty()) {
Chris@571 2687 defaultPitch = int(roundf(existing.begin()->getValue()));
matthiasm@293 2688 }
Chris@571 2689
Chris@571 2690 layer->splitNotesAt(pane, start);
Chris@571 2691 layer->splitNotesAt(pane, end);
Chris@571 2692 layer->deleteSelection(*k);
Chris@571 2693
Chris@571 2694 layer->addNoteOn(start, defaultPitch, 100);
Chris@571 2695 layer->addNoteOff(end, defaultPitch);
Chris@571 2696
Chris@571 2697 layer->mergeNotes(pane, *k, false);
Chris@240 2698 }
Chris@240 2699
matthiasm@293 2700 CommandHistory::getInstance()->endCompoundOperation();
Chris@240 2701 }
Chris@240 2702 }
Chris@240 2703
Chris@240 2704 void
Chris@0 2705 MainWindow::playSpeedChanged(int position)
Chris@0 2706 {
Chris@474 2707 PlaySpeedRangeMapper mapper;
Chris@0 2708
Chris@399 2709 double percent = m_playSpeed->mappedValue();
Chris@399 2710 double factor = mapper.getFactorForValue(percent);
Chris@0 2711
Chris@474 2712 int centre = m_playSpeed->defaultValue();
Chris@474 2713
Chris@474 2714 // Percentage is shown to 0dp if >100, to 1dp if <100; factor is
Chris@474 2715 // shown to 3sf
Chris@474 2716
Chris@474 2717 char pcbuf[30];
Chris@474 2718 char facbuf[30];
Chris@474 2719
Chris@474 2720 if (position == centre) {
Chris@0 2721 contextHelpChanged(tr("Playback speed: Normal"));
Chris@474 2722 } else if (position < centre) {
Chris@474 2723 sprintf(pcbuf, "%.1f", percent);
Chris@474 2724 sprintf(facbuf, "%.3g", 1.0 / factor);
Chris@474 2725 contextHelpChanged(tr("Playback speed: %1% (%2x slower)")
Chris@474 2726 .arg(pcbuf)
Chris@474 2727 .arg(facbuf));
Chris@0 2728 } else {
Chris@474 2729 sprintf(pcbuf, "%.0f", percent);
Chris@474 2730 sprintf(facbuf, "%.3g", factor);
Chris@474 2731 contextHelpChanged(tr("Playback speed: %1% (%2x faster)")
Chris@474 2732 .arg(pcbuf)
Chris@474 2733 .arg(facbuf));
Chris@0 2734 }
Chris@0 2735
Chris@474 2736 m_playSource->setTimeStretch(1.0 / factor); // factor is a speedup
Chris@0 2737
Chris@0 2738 updateMenuStates();
Chris@0 2739 }
Chris@0 2740
Chris@0 2741 void
Chris@0 2742 MainWindow::playSharpenToggled()
Chris@0 2743 {
Chris@0 2744 QSettings settings;
Chris@0 2745 settings.beginGroup("MainWindow");
Chris@0 2746 settings.setValue("playsharpen", m_playSharpen->isChecked());
Chris@0 2747 settings.endGroup();
Chris@0 2748
Chris@0 2749 playSpeedChanged(m_playSpeed->value());
justin@157 2750 // TODO: pitch gain?
Chris@0 2751 }
Chris@0 2752
Chris@0 2753 void
Chris@0 2754 MainWindow::playMonoToggled()
Chris@0 2755 {
Chris@0 2756 QSettings settings;
Chris@0 2757 settings.beginGroup("MainWindow");
Chris@0 2758 settings.setValue("playmono", m_playMono->isChecked());
Chris@0 2759 settings.endGroup();
Chris@0 2760
Chris@0 2761 playSpeedChanged(m_playSpeed->value());
justin@157 2762 // TODO: pitch gain?
Chris@0 2763 }
Chris@0 2764
Chris@0 2765 void
Chris@0 2766 MainWindow::speedUpPlayback()
Chris@0 2767 {
Chris@0 2768 int value = m_playSpeed->value();
Chris@0 2769 value = value + m_playSpeed->pageStep();
Chris@0 2770 if (value > m_playSpeed->maximum()) value = m_playSpeed->maximum();
Chris@0 2771 m_playSpeed->setValue(value);
Chris@0 2772 }
Chris@0 2773
Chris@0 2774 void
Chris@0 2775 MainWindow::slowDownPlayback()
Chris@0 2776 {
Chris@0 2777 int value = m_playSpeed->value();
Chris@0 2778 value = value - m_playSpeed->pageStep();
Chris@0 2779 if (value < m_playSpeed->minimum()) value = m_playSpeed->minimum();
Chris@0 2780 m_playSpeed->setValue(value);
Chris@0 2781 }
Chris@0 2782
Chris@0 2783 void
Chris@0 2784 MainWindow::restoreNormalPlayback()
Chris@0 2785 {
Chris@0 2786 m_playSpeed->setValue(m_playSpeed->defaultValue());
Chris@0 2787 }
Chris@0 2788
justin@157 2789 void
Chris@404 2790 MainWindow::audioGainChanged(float gain)
justin@159 2791 {
Chris@404 2792 double db = AudioLevel::multiplier_to_dB(gain);
Chris@404 2793 cerr << "gain = " << gain << " (" << db << " dB)" << endl;
Chris@404 2794 contextHelpChanged(tr("Audio Gain: %1 dB").arg(db));
Chris@417 2795 if (gain == 0.f) {
Chris@417 2796 m_analyser->setAudible(Analyser::Audio, false);
Chris@417 2797 } else {
Chris@417 2798 m_analyser->setAudible(Analyser::Audio, true);
Chris@417 2799 m_analyser->setGain(Analyser::Audio, gain);
Chris@417 2800 }
justin@159 2801 updateMenuStates();
justin@159 2802 }
justin@159 2803
justin@159 2804 void
Chris@404 2805 MainWindow::pitchGainChanged(float gain)
justin@159 2806 {
Chris@404 2807 double db = AudioLevel::multiplier_to_dB(gain);
Chris@404 2808 cerr << "gain = " << gain << " (" << db << " dB)" << endl;
Chris@404 2809 contextHelpChanged(tr("Pitch Gain: %1 dB").arg(db));
Chris@417 2810 if (gain == 0.f) {
Chris@417 2811 m_analyser->setAudible(Analyser::PitchTrack, false);
Chris@417 2812 } else {
Chris@417 2813 m_analyser->setAudible(Analyser::PitchTrack, true);
Chris@417 2814 m_analyser->setGain(Analyser::PitchTrack, gain);
Chris@417 2815 }
justin@157 2816 updateMenuStates();
justin@157 2817 }
justin@157 2818
justin@157 2819 void
Chris@404 2820 MainWindow::notesGainChanged(float gain)
justin@157 2821 {
Chris@404 2822 double db = AudioLevel::multiplier_to_dB(gain);
Chris@404 2823 cerr << "gain = " << gain << " (" << db << " dB)" << endl;
Chris@404 2824 contextHelpChanged(tr("Notes Gain: %1 dB").arg(db));
Chris@417 2825 if (gain == 0.f) {
Chris@417 2826 m_analyser->setAudible(Analyser::Notes, false);
Chris@417 2827 } else {
Chris@417 2828 m_analyser->setAudible(Analyser::Notes, true);
Chris@417 2829 m_analyser->setGain(Analyser::Notes, gain);
Chris@417 2830 }
justin@159 2831 updateMenuStates();
justin@159 2832 }
justin@159 2833
justin@159 2834 void
Chris@404 2835 MainWindow::audioPanChanged(float pan)
justin@159 2836 {
Chris@404 2837 contextHelpChanged(tr("Audio Pan: %1").arg(pan));
Chris@404 2838 m_analyser->setPan(Analyser::Audio, pan);
justin@160 2839 updateMenuStates();
justin@160 2840 }
justin@160 2841
justin@160 2842 void
Chris@404 2843 MainWindow::pitchPanChanged(float pan)
justin@160 2844 {
Chris@404 2845 contextHelpChanged(tr("Pitch Pan: %1").arg(pan));
Chris@404 2846 m_analyser->setPan(Analyser::PitchTrack, pan);
justin@160 2847 updateMenuStates();
justin@160 2848 }
justin@160 2849
justin@160 2850 void
Chris@404 2851 MainWindow::notesPanChanged(float pan)
justin@160 2852 {
Chris@404 2853 contextHelpChanged(tr("Notes Pan: %1").arg(pan));
Chris@404 2854 m_analyser->setPan(Analyser::Notes, pan);
justin@160 2855 updateMenuStates();
justin@160 2856 }
justin@160 2857
justin@160 2858 void
Chris@0 2859 MainWindow::updateVisibleRangeDisplay(Pane *p) const
Chris@0 2860 {
Chris@0 2861 if (!getMainModel() || !p) {
Chris@0 2862 return;
Chris@0 2863 }
Chris@0 2864
Chris@0 2865 bool haveSelection = false;
Chris@403 2866 sv_frame_t startFrame = 0, endFrame = 0;
Chris@0 2867
Chris@0 2868 if (m_viewManager && m_viewManager->haveInProgressSelection()) {
Chris@0 2869
Chris@0 2870 bool exclusive = false;
Chris@0 2871 Selection s = m_viewManager->getInProgressSelection(exclusive);
Chris@0 2872
Chris@0 2873 if (!s.isEmpty()) {
Chris@0 2874 haveSelection = true;
Chris@0 2875 startFrame = s.getStartFrame();
Chris@0 2876 endFrame = s.getEndFrame();
Chris@0 2877 }
Chris@0 2878 }
Chris@0 2879
Chris@0 2880 if (!haveSelection) {
Chris@0 2881 startFrame = p->getFirstVisibleFrame();
Chris@0 2882 endFrame = p->getLastVisibleFrame();
Chris@0 2883 }
Chris@0 2884
Chris@0 2885 RealTime start = RealTime::frame2RealTime
Chris@0 2886 (startFrame, getMainModel()->getSampleRate());
Chris@0 2887
Chris@0 2888 RealTime end = RealTime::frame2RealTime
Chris@0 2889 (endFrame, getMainModel()->getSampleRate());
Chris@0 2890
Chris@0 2891 RealTime duration = end - start;
Chris@0 2892
Chris@0 2893 QString startStr, endStr, durationStr;
Chris@0 2894 startStr = start.toText(true).c_str();
Chris@0 2895 endStr = end.toText(true).c_str();
Chris@0 2896 durationStr = duration.toText(true).c_str();
Chris@0 2897
Chris@0 2898 if (haveSelection) {
Chris@0 2899 m_myStatusMessage = tr("Selection: %1 to %2 (duration %3)")
Chris@0 2900 .arg(startStr).arg(endStr).arg(durationStr);
Chris@0 2901 } else {
Chris@0 2902 m_myStatusMessage = tr("Visible: %1 to %2 (duration %3)")
Chris@0 2903 .arg(startStr).arg(endStr).arg(durationStr);
Chris@0 2904 }
matthiasm@42 2905
Chris@362 2906 getStatusLabel()->setText(m_myStatusMessage);
Chris@0 2907 }
Chris@0 2908
Chris@0 2909 void
Chris@0 2910 MainWindow::updatePositionStatusDisplays() const
Chris@0 2911 {
Chris@0 2912 if (!statusBar()->isVisible()) return;
Chris@0 2913
Chris@0 2914 }
Chris@0 2915
Chris@0 2916 void
Chris@521 2917 MainWindow::monitoringLevelsChanged(float left, float right)
Chris@0 2918 {
Chris@0 2919 m_fader->setPeakLeft(left);
Chris@0 2920 m_fader->setPeakRight(right);
Chris@0 2921 }
Chris@0 2922
Chris@0 2923 void
Chris@399 2924 MainWindow::sampleRateMismatch(sv_samplerate_t ,
Chris@399 2925 sv_samplerate_t ,
Chris@399 2926 bool )
Chris@0 2927 {
Chris@0 2928 updateDescriptionLabel();
Chris@0 2929 }
Chris@0 2930
Chris@0 2931 void
Chris@0 2932 MainWindow::audioOverloadPluginDisabled()
Chris@0 2933 {
Chris@0 2934 QMessageBox::information
Chris@0 2935 (this, tr("Audio processing overload"),
Chris@0 2936 tr("<b>Overloaded</b><p>Audio effects plugin auditioning has been disabled due to a processing overload."));
Chris@0 2937 }
Chris@0 2938
Chris@0 2939 void
Chris@0 2940 MainWindow::audioTimeStretchMultiChannelDisabled()
Chris@0 2941 {
Chris@0 2942 static bool shownOnce = false;
Chris@0 2943 if (shownOnce) return;
Chris@0 2944 QMessageBox::information
Chris@0 2945 (this, tr("Audio processing overload"),
Chris@0 2946 tr("<b>Overloaded</b><p>Audio playback speed processing has been reduced to a single channel, due to a processing overload."));
Chris@0 2947 shownOnce = true;
Chris@0 2948 }
Chris@0 2949
Chris@0 2950 void
Chris@0 2951 MainWindow::layerRemoved(Layer *layer)
Chris@0 2952 {
Chris@0 2953 MainWindowBase::layerRemoved(layer);
Chris@0 2954 }
Chris@0 2955
Chris@0 2956 void
Chris@0 2957 MainWindow::layerInAView(Layer *layer, bool inAView)
Chris@0 2958 {
Chris@0 2959 MainWindowBase::layerInAView(layer, inAView);
Chris@0 2960 }
Chris@0 2961
Chris@0 2962 void
Chris@0 2963 MainWindow::modelAdded(Model *model)
Chris@0 2964 {
Chris@0 2965 MainWindowBase::modelAdded(model);
Chris@3 2966 DenseTimeValueModel *dtvm = qobject_cast<DenseTimeValueModel *>(model);
Chris@0 2967 if (dtvm) {
Chris@70 2968 cerr << "A dense time-value model (such as an audio file) has been loaded" << endl;
Chris@0 2969 }
Chris@0 2970 }
Chris@0 2971
Chris@0 2972 void
Chris@0 2973 MainWindow::modelAboutToBeDeleted(Model *model)
Chris@0 2974 {
Chris@0 2975 MainWindowBase::modelAboutToBeDeleted(model);
Chris@0 2976 }
Chris@0 2977
Chris@0 2978 void
Chris@0 2979 MainWindow::mainModelChanged(WaveFileModel *model)
Chris@0 2980 {
Chris@0 2981 m_panLayer->setModel(model);
Chris@0 2982
Chris@0 2983 MainWindowBase::mainModelChanged(model);
Chris@0 2984
Chris@479 2985 if (m_playTarget || m_audioIO) {
Chris@0 2986 connect(m_fader, SIGNAL(valueChanged(float)),
Chris@474 2987 this, SLOT(mainModelGainChanged(float)));
Chris@474 2988 }
Chris@474 2989 }
Chris@474 2990
Chris@474 2991 void
Chris@474 2992 MainWindow::mainModelGainChanged(float gain)
Chris@474 2993 {
Chris@474 2994 if (m_playTarget) {
Chris@474 2995 m_playTarget->setOutputGain(gain);
Chris@479 2996 } else if (m_audioIO) {
Chris@479 2997 m_audioIO->setOutputGain(gain);
Chris@0 2998 }
Chris@259 2999 }
Chris@259 3000
Chris@259 3001 void
Chris@323 3002 MainWindow::analyseNow()
Chris@323 3003 {
Chris@323 3004 cerr << "analyseNow called" << endl;
Chris@325 3005 if (!m_analyser) return;
Chris@325 3006
Chris@325 3007 CommandHistory::getInstance()->startCompoundOperation
Chris@325 3008 (tr("Analyse Audio"), true);
Chris@325 3009
Chris@325 3010 QString error = m_analyser->analyseExistingFile();
Chris@325 3011
Chris@325 3012 CommandHistory::getInstance()->endCompoundOperation();
Chris@325 3013
Chris@325 3014 if (error != "") {
Chris@325 3015 QMessageBox::warning
Chris@325 3016 (this,
Chris@325 3017 tr("Failed to analyse audio"),
Chris@325 3018 tr("<b>Analysis failed</b><p>%1</p>").arg(error),
Chris@325 3019 QMessageBox::Ok);
Chris@325 3020 }
Chris@323 3021 }
Chris@323 3022
Chris@323 3023 void
Chris@259 3024 MainWindow::analyseNewMainModel()
Chris@259 3025 {
Chris@259 3026 WaveFileModel *model = getMainModel();
Chris@260 3027
Chris@260 3028 cerr << "MainWindow::analyseNewMainModel: main model is " << model << endl;
Chris@260 3029
Chris@260 3030 cerr << "(document is " << m_document << ", it says main model is " << m_document->getMainModel() << ")" << endl;
Chris@259 3031
Chris@324 3032 if (!model) {
Chris@324 3033 cerr << "no main model!" << endl;
Chris@324 3034 return;
Chris@324 3035 }
Chris@324 3036
Chris@324 3037 if (!m_paneStack) {
Chris@324 3038 cerr << "no pane stack!" << endl;
Chris@324 3039 return;
Chris@324 3040 }
Chris@324 3041
Chris@324 3042 int pc = m_paneStack->getPaneCount();
Chris@324 3043 Pane *pane = 0;
Chris@324 3044 Pane *selectionStrip = 0;
Chris@324 3045
Chris@324 3046 if (pc < 2) {
Chris@324 3047 pane = m_paneStack->addPane();
Chris@324 3048 selectionStrip = m_paneStack->addPane();
Chris@324 3049 m_document->addLayerToView
Chris@324 3050 (selectionStrip,
Chris@324 3051 m_document->createMainModelLayer(LayerFactory::TimeRuler));
Chris@324 3052 } else {
Chris@324 3053 pane = m_paneStack->getPane(0);
Chris@324 3054 selectionStrip = m_paneStack->getPane(1);
Chris@324 3055 }
Chris@324 3056
Chris@362 3057 pane->setPlaybackFollow(PlaybackScrollPage);
Chris@362 3058
Chris@324 3059 if (selectionStrip) {
Chris@362 3060 selectionStrip->setPlaybackFollow(PlaybackScrollPage);
Chris@324 3061 selectionStrip->setFixedHeight(26);
Chris@324 3062 m_paneStack->sizePanesEqually();
Chris@324 3063 m_viewManager->clearToolModeOverrides();
Chris@324 3064 m_viewManager->setToolModeFor(selectionStrip,
Chris@324 3065 ViewManager::SelectMode);
Chris@324 3066 }
Chris@324 3067
Chris@324 3068 if (pane) {
Chris@324 3069
Chris@324 3070 disconnect(pane, SIGNAL(regionOutlined(QRect)),
Chris@324 3071 pane, SLOT(zoomToRegion(QRect)));
Chris@324 3072 connect(pane, SIGNAL(regionOutlined(QRect)),
Chris@324 3073 this, SLOT(regionOutlined(QRect)));
Chris@324 3074
Chris@324 3075 QString error = m_analyser->newFileLoaded
Chris@324 3076 (m_document, getMainModel(), m_paneStack, pane);
Chris@324 3077 if (error != "") {
Chris@324 3078 QMessageBox::warning
Chris@324 3079 (this,
Chris@324 3080 tr("Failed to analyse audio"),
Chris@324 3081 tr("<b>Analysis failed</b><p>%1</p>").arg(error),
Chris@324 3082 QMessageBox::Ok);
Chris@72 3083 }
Chris@72 3084 }
matthiasm@366 3085
matthiasm@366 3086 if (!m_withSpectrogram)
matthiasm@366 3087 {
matthiasm@366 3088 m_analyser->setVisible(Analyser::Spectrogram, false);
matthiasm@366 3089 }
matthiasm@366 3090
matthiasm@366 3091 if (!m_withSonification)
matthiasm@366 3092 {
matthiasm@366 3093 m_analyser->setAudible(Analyser::PitchTrack, false);
matthiasm@366 3094 m_analyser->setAudible(Analyser::Notes, false);
matthiasm@366 3095 }
Chris@419 3096
Chris@419 3097 updateLayerStatuses();
Chris@487 3098 documentRestored();
Chris@0 3099 }
Chris@0 3100
Chris@0 3101 void
Chris@0 3102 MainWindow::modelGenerationFailed(QString transformName, QString message)
Chris@0 3103 {
Chris@0 3104 if (message != "") {
Chris@0 3105
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 derived layer.<p>The layer transform \"%1\" failed:<p>%2")
Chris@0 3110 .arg(transformName).arg(message),
Chris@0 3111 QMessageBox::Ok);
Chris@0 3112 } else {
Chris@0 3113 QMessageBox::warning
Chris@0 3114 (this,
Chris@0 3115 tr("Failed to generate layer"),
Chris@0 3116 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 3117 .arg(transformName),
Chris@0 3118 QMessageBox::Ok);
Chris@0 3119 }
Chris@0 3120 }
Chris@0 3121
Chris@0 3122 void
Chris@355 3123 MainWindow::modelGenerationWarning(QString /* transformName */, QString message)
Chris@0 3124 {
Chris@0 3125 QMessageBox::warning
Chris@0 3126 (this, tr("Warning"), message, QMessageBox::Ok);
Chris@0 3127 }
Chris@0 3128
Chris@0 3129 void
Chris@0 3130 MainWindow::modelRegenerationFailed(QString layerName,
Chris@399 3131 QString transformName,
Chris@399 3132 QString message)
Chris@0 3133 {
Chris@0 3134 if (message != "") {
Chris@0 3135
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>%3")
Chris@0 3140 .arg(layerName).arg(transformName).arg(message),
Chris@0 3141 QMessageBox::Ok);
Chris@0 3142 } else {
Chris@0 3143 QMessageBox::warning
Chris@0 3144 (this,
Chris@0 3145 tr("Failed to regenerate layer"),
Chris@0 3146 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 3147 .arg(layerName).arg(transformName),
Chris@0 3148 QMessageBox::Ok);
Chris@0 3149 }
Chris@0 3150 }
Chris@0 3151
Chris@0 3152 void
Chris@0 3153 MainWindow::modelRegenerationWarning(QString layerName,
Chris@355 3154 QString /* transformName */,
Chris@355 3155 QString message)
Chris@0 3156 {
Chris@0 3157 QMessageBox::warning
Chris@0 3158 (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 3159 }
Chris@0 3160
Chris@0 3161 void
Chris@520 3162 MainWindow::alignmentFailed(QString message)
Chris@0 3163 {
Chris@0 3164 QMessageBox::warning
Chris@0 3165 (this,
Chris@0 3166 tr("Failed to calculate alignment"),
Chris@520 3167 tr("<b>Alignment calculation failed</b><p>Failed to calculate an audio alignment:<p>%1")
Chris@520 3168 .arg(message),
Chris@0 3169 QMessageBox::Ok);
Chris@0 3170 }
Chris@0 3171
Chris@0 3172 void
Chris@0 3173 MainWindow::rightButtonMenuRequested(Pane *pane, QPoint position)
Chris@0 3174 {
Chris@70 3175 // cerr << "MainWindow::rightButtonMenuRequested(" << pane << ", " << position.x() << ", " << position.y() << ")" << endl;
Chris@0 3176 m_paneStack->setCurrentPane(pane);
Chris@0 3177 m_rightButtonMenu->popup(position);
Chris@0 3178 }
Chris@0 3179
Chris@0 3180 void
Chris@355 3181 MainWindow::handleOSCMessage(const OSCMessage &)
Chris@0 3182 {
Chris@70 3183 cerr << "MainWindow::handleOSCMessage: Not implemented" << endl;
Chris@0 3184 }
Chris@0 3185
Chris@0 3186 void
Chris@0 3187 MainWindow::mouseEnteredWidget()
Chris@0 3188 {
Chris@3 3189 QWidget *w = qobject_cast<QWidget *>(sender());
Chris@0 3190 if (!w) return;
Chris@0 3191
Chris@0 3192 if (w == m_fader) {
Chris@0 3193 contextHelpChanged(tr("Adjust the master playback level"));
Chris@0 3194 } else if (w == m_playSpeed) {
Chris@0 3195 contextHelpChanged(tr("Adjust the master playback speed"));
Chris@0 3196 } else if (w == m_playSharpen && w->isEnabled()) {
Chris@0 3197 contextHelpChanged(tr("Toggle transient sharpening for playback time scaling"));
Chris@0 3198 } else if (w == m_playMono && w->isEnabled()) {
Chris@0 3199 contextHelpChanged(tr("Toggle mono mode for playback time scaling"));
Chris@0 3200 }
Chris@0 3201 }
Chris@0 3202
Chris@0 3203 void
Chris@0 3204 MainWindow::mouseLeftWidget()
Chris@0 3205 {
Chris@0 3206 contextHelpChanged("");
Chris@0 3207 }
Chris@0 3208
Chris@0 3209 void
Chris@0 3210 MainWindow::website()
Chris@0 3211 {
Chris@4 3212 //!!! todo: URL!
Chris@4 3213 openHelpUrl(tr("http://code.soundsoftware.ac.uk/projects/tony/"));
Chris@0 3214 }
Chris@0 3215
Chris@0 3216 void
Chris@0 3217 MainWindow::help()
Chris@0 3218 {
Chris@4 3219 //!!! todo: help URL!
matthiasm@369 3220 openHelpUrl(tr("http://code.soundsoftware.ac.uk/projects/tony/wiki/Reference"));
Chris@0 3221 }
Chris@0 3222
Chris@0 3223 void
Chris@0 3224 MainWindow::about()
Chris@0 3225 {
Chris@0 3226 bool debug = false;
Chris@0 3227 QString version = "(unknown version)";
Chris@0 3228
Chris@0 3229 #ifdef BUILD_DEBUG
Chris@0 3230 debug = true;
Chris@0 3231 #endif
Chris@0 3232 version = tr("Release %1").arg(TONY_VERSION);
Chris@0 3233
Chris@0 3234 QString aboutText;
Chris@0 3235
Chris@0 3236 aboutText += tr("<h3>About Tony</h3>");
Chris@0 3237 aboutText += tr("<p>Tony is a program for interactive note and pitch analysis and annotation.</p>");
Chris@0 3238 aboutText += tr("<p>%1 : %2 configuration</p>")
Chris@0 3239 .arg(version)
Chris@0 3240 .arg(debug ? tr("Debug") : tr("Release"));
Chris@212 3241 aboutText += tr("<p>Using Qt framework version %1.</p>")
Chris@206 3242 .arg(QT_VERSION_STR);
Chris@0 3243
Chris@0 3244 aboutText +=
Chris@508 3245 "<p>Copyright &copy; 2005&ndash;2015 Chris Cannam, Queen Mary University of London, and the Tony project authors: Matthias Mauch, George Fazekas, Justin Salamon, and Rachel Bittner.</p>"
Chris@212 3246 "<p>pYIN analysis plugin written by Matthias Mauch.</p>"
Chris@90 3247 "<p>This program is free software; you can redistribute it and/or "
Chris@90 3248 "modify it under the terms of the GNU General Public License as "
Chris@90 3249 "published by the Free Software Foundation; either version 2 of the "
Chris@0 3250 "License, or (at your option) any later version.<br>See the file "
Chris@0 3251 "COPYING included with this distribution for more information.</p>";
Chris@0 3252
Chris@0 3253 QMessageBox::about(this, tr("About %1").arg(QApplication::applicationName()), aboutText);
Chris@0 3254 }
Chris@0 3255
Chris@0 3256 void
Chris@0 3257 MainWindow::keyReference()
Chris@0 3258 {
Chris@0 3259 m_keyReference->show();
Chris@0 3260 }
Chris@0 3261
Chris@231 3262 void
Chris@231 3263 MainWindow::newerVersionAvailable(QString version)
Chris@231 3264 {
Chris@231 3265 //!!! nicer URL would be nicer
Chris@231 3266 QSettings settings;
Chris@231 3267 settings.beginGroup("NewerVersionWarning");
Chris@231 3268 QString tag = QString("version-%1-available-show").arg(version);
Chris@231 3269 if (settings.value(tag, true).toBool()) {
Chris@231 3270 QString title(tr("Newer version available"));
Chris@231 3271 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 3272 QMessageBox::information(this, title, text);
Chris@231 3273 settings.setValue(tag, false);
Chris@231 3274 }
Chris@231 3275 settings.endGroup();
Chris@231 3276 }
Chris@231 3277
matthiasm@356 3278 void
matthiasm@356 3279 MainWindow::ffwd()
matthiasm@356 3280 {
matthiasm@356 3281 if (!getMainModel()) return;
matthiasm@356 3282
Chris@403 3283 sv_frame_t frame = m_viewManager->getPlaybackFrame();
matthiasm@356 3284 ++frame;
matthiasm@356 3285
Chris@403 3286 sv_samplerate_t sr = getMainModel()->getSampleRate();
matthiasm@356 3287
matthiasm@356 3288 // The step is supposed to scale and be as wide as a step of
matthiasm@357 3289 // m_defaultFfwdRwdStep seconds at zoom level 720 and sr = 44100
Chris@540 3290
Chris@540 3291 ZoomLevel zoom = m_viewManager->getGlobalZoom();
Chris@540 3292 double framesPerPixel = 1.0;
Chris@540 3293 if (zoom.zone == ZoomLevel::FramesPerPixel) {
Chris@540 3294 framesPerPixel = zoom.level;
Chris@540 3295 } else {
Chris@540 3296 framesPerPixel = 1.0 / zoom.level;
Chris@540 3297 }
Chris@540 3298 double defaultFramesPerPixel = (720 * 44100) / sr;
Chris@540 3299 double scaler = framesPerPixel / defaultFramesPerPixel;
Chris@403 3300 RealTime step = m_defaultFfwdRwdStep * scaler;
Chris@403 3301
matthiasm@356 3302 frame = RealTime::realTime2Frame
Chris@403 3303 (RealTime::frame2RealTime(frame, sr) + step, sr);
Chris@403 3304
Chris@403 3305 if (frame > getMainModel()->getEndFrame()) {
matthiasm@356 3306 frame = getMainModel()->getEndFrame();
matthiasm@356 3307 }
Chris@403 3308
matthiasm@356 3309 if (frame < 0) frame = 0;
matthiasm@356 3310
matthiasm@356 3311 if (m_viewManager->getPlaySelectionMode()) {
Chris@403 3312 frame = m_viewManager->constrainFrameToSelection(frame);
matthiasm@356 3313 }
matthiasm@356 3314
matthiasm@356 3315 m_viewManager->setPlaybackFrame(frame);
matthiasm@356 3316
Chris@403 3317 if (frame == getMainModel()->getEndFrame() &&
matthiasm@356 3318 m_playSource &&
matthiasm@356 3319 m_playSource->isPlaying() &&
matthiasm@356 3320 !m_viewManager->getPlayLoopMode()) {
matthiasm@356 3321 stop();
matthiasm@356 3322 }
matthiasm@356 3323 }
matthiasm@356 3324
matthiasm@356 3325 void
matthiasm@356 3326 MainWindow::rewind()
matthiasm@356 3327 {
matthiasm@356 3328 if (!getMainModel()) return;
matthiasm@356 3329
Chris@403 3330 sv_frame_t frame = m_viewManager->getPlaybackFrame();
matthiasm@356 3331 if (frame > 0) --frame;
matthiasm@356 3332
Chris@403 3333 sv_samplerate_t sr = getMainModel()->getSampleRate();
matthiasm@356 3334
matthiasm@356 3335 // The step is supposed to scale and be as wide as a step of
matthiasm@357 3336 // m_defaultFfwdRwdStep seconds at zoom level 720 and sr = 44100
Chris@540 3337
Chris@540 3338 ZoomLevel zoom = m_viewManager->getGlobalZoom();
Chris@540 3339 double framesPerPixel = 1.0;
Chris@540 3340 if (zoom.zone == ZoomLevel::FramesPerPixel) {
Chris@540 3341 framesPerPixel = zoom.level;
Chris@540 3342 } else {
Chris@540 3343 framesPerPixel = 1.0 / zoom.level;
Chris@540 3344 }
Chris@540 3345 double defaultFramesPerPixel = (720 * 44100) / sr;
Chris@540 3346 double scaler = framesPerPixel / defaultFramesPerPixel;
Chris@403 3347 RealTime step = m_defaultFfwdRwdStep * scaler;
Chris@403 3348
matthiasm@356 3349 frame = RealTime::realTime2Frame
Chris@403 3350 (RealTime::frame2RealTime(frame, sr) - step, sr);
Chris@403 3351
Chris@403 3352 if (frame < getMainModel()->getStartFrame()) {
matthiasm@356 3353 frame = getMainModel()->getStartFrame();
matthiasm@356 3354 }
matthiasm@356 3355
matthiasm@356 3356 if (frame < 0) frame = 0;
matthiasm@356 3357
matthiasm@356 3358 if (m_viewManager->getPlaySelectionMode()) {
Chris@403 3359 frame = m_viewManager->constrainFrameToSelection(frame);
matthiasm@356 3360 }
matthiasm@356 3361
matthiasm@356 3362 m_viewManager->setPlaybackFrame(frame);
matthiasm@356 3363 }