Mercurial > hg > sonic-visualiser
diff main/MainWindow.cpp @ 180:98ba77e0d897
* Merge from sv-match-alignment branch (excluding alignment-specific document).
- add aggregate wave model (not yet complete enough to be added as a true
model in a layer, but there's potential)
- add play solo mode
- add alignment model -- unused in plain SV
- fix two plugin leaks
- add m3u playlist support (opens all files at once, potentially hazardous)
- fix retrieval of pre-encoded URLs
- add ability to resample audio files on import, so as to match rates with
other files previously loaded; add preference for same
- add preliminary support in transform code for range and rate of transform
input
- reorganise preferences dialog, move dark-background option to preferences,
add option for temporary directory location
author | Chris Cannam |
---|---|
date | Fri, 28 Sep 2007 13:56:38 +0000 |
parents | 5bde373ad5ca |
children | ebd906049fb6 |
line wrap: on
line diff
--- a/main/MainWindow.cpp Fri Sep 21 09:13:11 2007 +0000 +++ b/main/MainWindow.cpp Fri Sep 28 13:56:38 2007 +0000 @@ -47,8 +47,8 @@ #include "audioio/AudioCallbackPlayTarget.h" #include "audioio/AudioTargetFactory.h" #include "audioio/PlaySpeedRangeMapper.h" -#include "data/fileio/AudioFileReaderFactory.h" #include "data/fileio/DataFileReaderFactory.h" +#include "data/fileio/PlaylistFileReader.h" #include "data/fileio/WavFileWriter.h" #include "data/fileio/CSVFileWriter.h" #include "data/fileio/BZipFileDevice.h" @@ -65,6 +65,9 @@ #include "base/ColourDatabase.h" #include "osc/OSCQueue.h" +//!!! +#include "data/model/AggregateWaveModel.h" + // For version information #include "vamp/vamp.h" #include "vamp-sdk/PluginBase.h" @@ -177,6 +180,14 @@ connect(m_viewManager, SIGNAL(inProgressSelectionChanged()), this, SLOT(inProgressSelectionChanged())); + Preferences::BackgroundMode mode = + Preferences::getInstance()->getBackgroundMode(); + m_initialDarkBackground = m_viewManager->getGlobalDarkBackground(); + if (mode != Preferences::BackgroundFromTheme) { + m_viewManager->setGlobalDarkBackground + (mode == Preferences::DarkBackground); + } + m_descriptionLabel = new QLabel; QScrollArea *scroll = new QScrollArea(frame); @@ -850,15 +861,6 @@ menu->addSeparator(); - action = new QAction(tr("Use Dar&k Background"), this); - action->setStatusTip(tr("Switch between light and dark background colour schemes")); - connect(action, SIGNAL(triggered()), this, SLOT(toggleDarkBackground())); - action->setCheckable(true); - action->setChecked(m_viewManager->getGlobalDarkBackground()); - menu->addAction(action); - - menu->addSeparator(); - action = new QAction(tr("Show &Zoom Wheels"), this); action->setShortcut(tr("Z")); action->setStatusTip(tr("Show thumbwheels for zooming horizontally and vertically")); @@ -1684,9 +1686,21 @@ connect(plAction, SIGNAL(triggered()), this, SLOT(playLoopToggled())); connect(this, SIGNAL(canPlay(bool)), plAction, SLOT(setEnabled(bool))); + QAction *soAction = toolbar->addAction(il.load("solo"), + tr("Solo Current Pane")); + soAction->setCheckable(true); + soAction->setChecked(m_viewManager->getPlaySoloMode()); + soAction->setShortcut(tr("o")); + soAction->setStatusTip(tr("Solo the current pane during playback")); + connect(m_viewManager, SIGNAL(playSoloModeChanged(bool)), + soAction, SLOT(setChecked(bool))); + connect(soAction, SIGNAL(triggered()), this, SLOT(playSoloToggled())); + connect(this, SIGNAL(canPlay(bool)), soAction, SLOT(setEnabled(bool))); + m_keyReference->registerShortcut(playAction); m_keyReference->registerShortcut(psAction); m_keyReference->registerShortcut(plAction); + m_keyReference->registerShortcut(soAction); m_keyReference->registerShortcut(m_rwdAction); m_keyReference->registerShortcut(m_ffwdAction); m_keyReference->registerShortcut(rwdStartAction); @@ -1695,6 +1709,7 @@ menu->addAction(playAction); menu->addAction(psAction); menu->addAction(plAction); + menu->addAction(soAction); menu->addSeparator(); menu->addAction(m_rwdAction); menu->addAction(m_ffwdAction); @@ -1706,6 +1721,7 @@ m_rightButtonPlaybackMenu->addAction(playAction); m_rightButtonPlaybackMenu->addAction(psAction); m_rightButtonPlaybackMenu->addAction(plAction); + m_rightButtonPlaybackMenu->addAction(soAction); m_rightButtonPlaybackMenu->addSeparator(); m_rightButtonPlaybackMenu->addAction(m_rwdAction); m_rightButtonPlaybackMenu->addAction(m_ffwdAction); @@ -1996,10 +2012,72 @@ } void +MainWindow::playSoloToggled() +{ + QAction *action = dynamic_cast<QAction *>(sender()); + + if (action) { + m_viewManager->setPlaySoloMode(action->isChecked()); + } else { + m_viewManager->setPlaySoloMode(!m_viewManager->getPlaySoloMode()); + } + + if (!m_viewManager->getPlaySoloMode()) { + m_viewManager->setPlaybackModel(0); + if (m_playSource) { + m_playSource->clearSoloModelSet(); + } + } +} + +void MainWindow::currentPaneChanged(Pane *p) { updateMenuStates(); updateVisibleRangeDisplay(p); + + if (!p) return; + + if (!(m_viewManager && + m_playSource && + m_viewManager->getPlaySoloMode())) { + if (m_viewManager) m_viewManager->setPlaybackModel(0); + return; + } + + Model *prevPlaybackModel = m_viewManager->getPlaybackModel(); + + std::set<Model *> soloModels; + + for (int i = 0; i < p->getLayerCount(); ++i) { + Layer *layer = p->getLayer(i); + if (dynamic_cast<TimeRulerLayer *>(layer)) { + continue; + } + if (layer && layer->getModel()) { + Model *model = layer->getModel(); + if (dynamic_cast<RangeSummarisableTimeValueModel *>(model)) { + m_viewManager->setPlaybackModel(model); + } + soloModels.insert(model); + } + } + + RangeSummarisableTimeValueModel *a = + dynamic_cast<RangeSummarisableTimeValueModel *>(prevPlaybackModel); + RangeSummarisableTimeValueModel *b = + dynamic_cast<RangeSummarisableTimeValueModel *>(m_viewManager-> + getPlaybackModel()); + + m_playSource->setSoloModelSet(soloModels); + + if (a && b && (a != b)) { + int frame = m_playSource->getCurrentPlayingFrame(); + //!!! I don't really believe that these functions are the right way around + int rframe = a->alignFromReference(frame); + int bframe = b->alignToReference(rframe); + if (m_playSource->isPlaying()) m_playSource->play(bframe); + } } void @@ -2680,7 +2758,13 @@ m_openingAudioFile = true; - WaveFileModel *newModel = new WaveFileModel(path, location); + size_t rate = 0; + + if (Preferences::getInstance()->getResampleOnLoad()) { + rate = m_playSource->getSourceSampleRate(); + } + + WaveFileModel *newModel = new WaveFileModel(path, location, rate); if (!newModel->isOK()) { delete newModel; @@ -2784,9 +2868,57 @@ } m_openingAudioFile = false; + currentPaneChanged(m_paneStack->getCurrentPane()); + return FileOpenSucceeded; } +MainWindow::FileOpenStatus +MainWindow::openPlaylistFile(QString path, AudioFileOpenMode mode) +{ + return openPlaylistFile(path, path, mode); +} + +MainWindow::FileOpenStatus +MainWindow::openPlaylistFile(QString path, QString location, AudioFileOpenMode mode) +{ + if (!(QFileInfo(path).exists() && + QFileInfo(path).isFile() && + QFileInfo(path).isReadable())) { + return FileOpenFailed; + } + + std::set<QString> extensions; + PlaylistFileReader::getSupportedExtensions(extensions); + QString extension = QFileInfo(path).suffix(); + if (extensions.find(extension) == extensions.end()) return FileOpenFailed; + + PlaylistFileReader reader(path); + if (!reader.isOK()) return FileOpenFailed; + + PlaylistFileReader::Playlist playlist = reader.load(); + + bool someSuccess = false; + + for (PlaylistFileReader::Playlist::const_iterator i = playlist.begin(); + i != playlist.end(); ++i) { + + FileOpenStatus status = openURL(*i, mode); + + if (status == FileOpenCancelled) { + return FileOpenCancelled; + } + + if (status == FileOpenSucceeded) { + someSuccess = true; + mode = CreateAdditionalModel; + } + } + + if (someSuccess) return FileOpenSucceeded; + else return FileOpenFailed; +} + void MainWindow::createPlayTarget() { @@ -2965,12 +3097,15 @@ } else { - if (openAudioFile(path, AskUser) == FileOpenFailed) { - - if (!canImportLayer || (openLayerFile(path) == FileOpenFailed)) { - - QMessageBox::critical(this, tr("Failed to open file"), - tr("File \"%1\" could not be opened").arg(path)); + if (openPlaylistFile(path, AskUser) == FileOpenFailed) { + + if (openAudioFile(path, AskUser) == FileOpenFailed) { + + if (!canImportLayer || (openLayerFile(path) == FileOpenFailed)) { + + QMessageBox::critical(this, tr("Failed to open file"), + tr("File \"%1\" could not be opened").arg(path)); + } } } } @@ -3033,31 +3168,38 @@ } else { - if (openAudioFile(path, AskUser) == FileOpenFailed) { - - bool canImportLayer = (getMainModel() != 0 && - m_paneStack != 0 && - m_paneStack->getCurrentPane() != 0); - - if (!canImportLayer || (openLayerFile(path) == FileOpenFailed)) { - - QMessageBox::critical(this, tr("Failed to open file"), - tr("File \"%1\" could not be opened").arg(path)); + if (openPlaylistFile(path, AskUser) == FileOpenFailed) { + + if (openAudioFile(path, AskUser) == FileOpenFailed) { + + bool canImportLayer = (getMainModel() != 0 && + m_paneStack != 0 && + m_paneStack->getCurrentPane() != 0); + + if (!canImportLayer || (openLayerFile(path) == FileOpenFailed)) { + + QMessageBox::critical(this, tr("Failed to open file"), + tr("File \"%1\" could not be opened").arg(path)); + } } } } } MainWindow::FileOpenStatus -MainWindow::openURL(QUrl url) +MainWindow::openURL(QUrl url, AudioFileOpenMode mode) { if (url.scheme().toLower() == "file") { - return openSomeFile(url.toLocalFile()); + + return openSomeFile(url.toLocalFile(), mode); + } else if (!RemoteFile::canHandleScheme(url)) { + QMessageBox::critical(this, tr("Unsupported scheme in URL"), tr("The URL scheme \"%1\" is not supported") .arg(url.scheme())); return FileOpenFailed; + } else { RemoteFile rf(url); rf.wait(); @@ -3068,7 +3210,8 @@ return FileOpenFailed; } FileOpenStatus status; - if ((status = openSomeFile(rf.getLocalFilename(), url.toString())) != + if ((status = openSomeFile(rf.getLocalFilename(), url.toString(), + mode)) != FileOpenSucceeded) { rf.deleteLocalFile(); } @@ -3077,6 +3220,49 @@ } MainWindow::FileOpenStatus +MainWindow::openURL(QString ustr, AudioFileOpenMode mode) +{ + // This function is used when we don't know whether the string is + // an encoded or human-readable url + + QUrl url(ustr); + + if (url.scheme().toLower() == "file") { + + return openSomeFile(url.toLocalFile(), mode); + + } else if (!RemoteFile::canHandleScheme(url)) { + + QMessageBox::critical(this, tr("Unsupported scheme in URL"), + tr("The URL scheme \"%1\" is not supported") + .arg(url.scheme())); + return FileOpenFailed; + + } else { + RemoteFile rf(url); + rf.wait(); + if (!rf.isOK()) { + // rf was created on the assumption that ustr was + // human-readable. Let's try again, this time assuming it + // was already encoded. + std::cerr << "MainWindow::openURL: Failed to retrieve URL \"" + << ustr.toStdString() << "\" as human-readable URL; " + << "trying again treating it as encoded URL" + << std::endl; + url.setEncodedUrl(ustr.toAscii()); + return openURL(url, mode); + } + + FileOpenStatus status; + if ((status = openSomeFile(rf.getLocalFilename(), ustr, mode)) != + FileOpenSucceeded) { + rf.deleteLocalFile(); + } + return status; + } +} + +MainWindow::FileOpenStatus MainWindow::openSomeFile(QString path, AudioFileOpenMode mode) { return openSomeFile(path, path, mode); @@ -3092,7 +3278,9 @@ m_paneStack != 0 && m_paneStack->getCurrentPane() != 0); - if ((status = openAudioFile(path, location, mode)) != FileOpenFailed) { + if ((status = openPlaylistFile(path, location, mode)) != FileOpenFailed) { + return status; + } else if ((status = openAudioFile(path, location, mode)) != FileOpenFailed) { return status; } else if ((status = openSessionFile(path, location)) != FileOpenFailed) { return status; @@ -3546,23 +3734,6 @@ } void -MainWindow::toggleDarkBackground() -{ - if (!m_viewManager) return; - - m_viewManager->setGlobalDarkBackground - (!m_viewManager->getGlobalDarkBackground()); - - if (m_viewManager->getGlobalDarkBackground()) { - m_panLayer->setBaseColour - (ColourDatabase::getInstance()->getColourIndex(tr("Bright Green"))); - } else { - m_panLayer->setBaseColour - (ColourDatabase::getInstance()->getColourIndex(tr("Green"))); - } -} - -void MainWindow::preferenceChanged(PropertyContainer::PropertyName name) { if (name == "Property Box Layout") { @@ -3574,7 +3745,24 @@ m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout); } } - } + } else if (name == "Background Mode" && m_viewManager) { + Preferences::BackgroundMode mode = + Preferences::getInstance()->getBackgroundMode(); + if (mode == Preferences::BackgroundFromTheme) { + m_viewManager->setGlobalDarkBackground(m_initialDarkBackground); + } else if (mode == Preferences::DarkBackground) { + m_viewManager->setGlobalDarkBackground(true); + } else { + m_viewManager->setGlobalDarkBackground(false); + } + if (m_viewManager->getGlobalDarkBackground()) { + m_panLayer->setBaseColour + (ColourDatabase::getInstance()->getColourIndex(tr("Bright Green"))); + } else { + m_panLayer->setBaseColour + (ColourDatabase::getInstance()->getColourIndex(tr("Green"))); + } + } } void @@ -4413,6 +4601,9 @@ MainWindow::modelAboutToBeDeleted(Model *model) { // std::cerr << "MainWindow::modelAboutToBeDeleted(" << model << ")" << std::endl; + if (model == m_viewManager->getPlaybackModel()) { + m_viewManager->setPlaybackModel(0); + } m_playSource->removeModel(model); FFTDataServer::modelAboutToBeDeleted(model); } @@ -4650,6 +4841,19 @@ } } + } else if (message.getMethod() == "solo") { + + if (message.getArgCount() == 1 && + message.getArg(0).canConvert(QVariant::String)) { + + QString str = message.getArg(0).toString(); + if (str == "on") { + m_viewManager->setPlaySoloMode(true); + } else if (str == "off") { + m_viewManager->setPlaySoloMode(false); + } + } + } else if (message.getMethod() == "select" || message.getMethod() == "addselect") {